广告

JavaScript 深度解析 JSON.parse 与 JSON.stringify:原理、常见坑点与实战技巧

1. JSON.parse 的原理与执行流程

1.1 JSON.parse 的核心原理

JSON.parse 将符合 JSON 规范的文本 转换成对应的 JavaScript 值、对象或数组,其解析过程依赖于浏览器或运行时对 词法分析和语法分析 的支持,并在内部构建一个对象树。

在解析阶段,字符串中的关键字如 null、true、false 会被映射成 JavaScript 的 原始值,其他被引号包裹的文本则转为 字符串,数字遵循 IEEE-754 双精度浮点数 的表示规则。

需要注意的是,JSON.parse 只能接受 严格符合 JSON 的文本,否则会抛出 SyntaxError,并且只有在正确的文本中才能得到正确的 对象结构

1.2 reviver 回调的作用与使用场景

reviver 是可选的回调函数,用于在解析过程中对每个键值对进行 转换与过滤,从而实现自定义的对象构造。

通过 reviver 的返回值,可以 修改或替换 某些字段的值,也可以返回 undefined 来删除当前属性。

典型场景包括将日期字符串转换为 Date 对象、对数字进行单位处理、以及对缺失字段进行默认填充,从而让结果更加符合业务模型。

const json = '{"date":"2024-01-01T12:34:56Z","count":5}';
const obj = JSON.parse(json, (k, v) => {if (typeof v === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(v)) {return new Date(v);}return v;
});

2. JSON.stringify 的原理与输出机制

2.1 序列化的核心算法

JSON.stringify 将 JavaScript 值转换为 JSON 字文本,核心在于确保值满足可序列化条件:对象、数组、字符串、数字、布尔值和 null;而 函数、Symbol、undefined 等在对象属性中通常被忽略或在数组中被替换为 null。

序列化过程会递归遍历对象和数组,遇见 循环引用 时会抛出 TypeError: Converting circular structure to JSON,因此在复杂数据结构上需要提前处理或采用替代方案。

输出的文本是一个独立的 文本表示,便于网络传输、缓存或日志记录,确保前后端数据交换的一致性。

const data = { a: 1, b: 'text', c: undefined, d: () => 42 };
JSON.stringify(data); // "{"a":1,"b":"text"}"

2.2 replacer 与 space 的作用

通过 replacer 可以在序列化时对值进行筛选或变换,数组保留顺序,函数用于修改值,也可以传入一个数组作为白名单仅序列化这些键。

space 参数 用于控制输出的缩进和可读性,数值越大输出越美观,字符串会被用作缩进单位,便于调试和日志查看。

const obj = { a: 1, b: undefined, c: 3 };
const pretty = JSON.stringify(obj, null, 2);
console.log(pretty);
/* {"a": 1,"c": 3
} */

3. 常见坑点与错误场景

3.1 无效 JSON 字符与非法格式

最常见的坑是传入一个 非严格 JSON 的文本,例如使用单引号、尾随逗号、或注释等,这些都不被 JSON.parse 接受,需要使用标准的 双引号、无尾随逗号、无注释 的文本。

在遇到格式错误时,通常应使用 try/catch 捕获异常并记录,避免页面崩溃,同时给出友好提示。

try {const obj = JSON.parse("{'a':1, 'b':2}");
} catch (e) {console.error('JSON parsing failed:', e.message);
}

3.2 undefined、函数与 symbol 的处理

JSON.stringify 对对象属性中的 undefined、函数和 Symbol 通常会进行特殊处理:在对象中会被忽略,而在数组中会被替换为 null,这也是前后端通信时需要注意的点。

为了保留必要信息,可以通过 自定义 replacer 函数来显式处理这些值,或在序列化前将其替换为可序列化的形式。

const payload = { a: undefined, b: 42, c: Symbol('id') };
console.log(JSON.stringify(payload)); // "{"b":42}"

3.3 循环引用与性能风险

当对象存在循环引用时,JSON.stringify 会抛出 TypeError: Converting circular structure to JSON,因此在序列化前需要进行去循环处理,或采用替代方案如循环检测工具。

对于大对象,序列化过程可能带来较高的 CPU 使用和内存占用风险,对输出大小进行分段或采用流式处理可提升性能,尤其在日志或网络传输中尤为重要。

4. 实战技巧:提升前后端数据交互的稳健性

4.1 日期与自定义对象的序列化策略

日期在 JSON 中没有原生类型,常用的做法是序列化为 ISO 8601 字符串,并在解析阶段使用 reviver 将其转换回 Date 对象,以便日后计算和展示。

自定义对象通常需要提升可序列化性,可以通过实现 toJSON 方法,让对象在序列化时返回一个简化的、可序列化的结构。

const dt = { date: new Date('2024-01-01T12:00:00Z') };
dt.toJSON = function() { return { date: this.date.toISOString() }; };
JSON.stringify(dt); // "{"date":"2024-01-01T12:00:00.000Z"}"

4.2 可靠的错误处理与回退策略

对前端从服务器接收到的 JSON 字符串,最好以 try/catch 进行容错处理,并在失败时提供默认值或回退策略,避免影响后续逻辑。

服务端在返回 JSON 时应确保 Content-Type: application/json,客户端则可通过读取原始文本后再进行解析,以防止自动处理带来的异常。

async function fetchData(url) {const res = await fetch(url);const text = await res.text();try {return JSON.parse(text);} catch {return { error: 'Invalid JSON', data: null };}
}

4.3 使用 reviver/replacer 的典型用法示例

结合 reviver 与 replacer,可以实现复杂的数据转换与筛选逻辑,以下示例演示将日期字符串转为 Date,同时过滤掉不需要的键。

const schema = (key, value) => {if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) {return new Date(value);}if (key === 'password') return undefined;return value;
};
const raw = '{ "name":"Alice","updated":"2024-01-02T03:04:05Z","password":"secret"}';
const parsed = JSON.parse(raw, schema);
const serialized = JSON.stringify(parsed, null, 2);
console.log(serialized);

5. 典型案例与代码示例

5.1 将 JSON 字符串解析为对象并安全访问

在实际应用中,往往需要从服务器获取的文本先进行解析,然后再进行安全访问,核心点在于对无效结构的容错能力。

通过 try/catch 来确保解析失败时不会中断后续流程,并可通过默认对象保证界面稳定。

JavaScript 深度解析 JSON.parse 与 JSON.stringify:原理、常见坑点与实战技巧

function safeParse(json) {try {return JSON.parse(json);} catch {return { error: 'invalid json' };}
}
const data = safeParse('{ "a":1,"b":2 }');
console.log(data.a); // 1

5.2 将对象序列化为字符串并美化输出

为了日志、调试和界面展示,常需要将复杂对象序列化为结构化文本,结合 replacer 与 space 可以实现灵活控制。

const user = { id: 1, name: 'Bob', roles: ['admin','user'] };
const json = JSON.stringify(user, null, 2);
console.log(json);
/* {"id": 1,"name": "Bob","roles": ["admin","user"]
} */

广告