JSON.stringify 和 JSON.parse 有哪些细节和限制?
简化版
JSON.stringify 把 JavaScript 值转成 JSON 字符串,JSON.parse 把 JSON 字符串解析成值。它们只适合 JSON 支持的数据:对象、数组、字符串、数字、布尔值和 null。
函数、undefined、Symbol、循环引用、BigInt 等都要特别处理。对象里这些值通常会被忽略,数组里会变成 null,循环引用会直接抛错。
详细版
常见行为:
JSON.stringify({
a: undefined,
b: () => {},
c: Symbol("x"),
d: null
});
// {"d":null}
JSON.stringify([undefined, () => {}, Symbol("x")]);
// [null,null,null]
循环引用会报错:
const obj = {};
obj.self = obj;
JSON.stringify(obj); // TypeError
JSON.stringify 支持 replacer 和 space,JSON.parse 支持 reviver。
const text = JSON.stringify({ password: "x", name: "Ada" }, (key, value) => {
return key === "password" ? undefined : value;
}, 2);
const data = JSON.parse('{"createdAt":"2026-07-29"}', (key, value) => {
return key === "createdAt" ? new Date(value) : value;
});
完整版教学
一、JSON 不是 JavaScript 对象的完整镜像
JSON 是一种数据交换格式,不是 JavaScript 内存对象的完整快照。
它支持的数据类型很有限:
| JSON 类型 | JavaScript 对应值 |
|---|---|
| object | 普通对象 |
| array | 数组 |
| string | 字符串 |
| number | 有限数字 |
| boolean | 布尔值 |
| null | null |
函数、原型、访问器属性、Map、Set、Date 的对象身份都不会被完整保留。
看到 JSON 序列化,要立刻想到“跨进程传数据”,不是“深拷贝一切”。
二、对象和数组里特殊值表现不同
对象属性中,undefined、函数、Symbol 会被忽略。
const obj = {
a: 1,
b: undefined,
c() {},
d: Symbol("d")
};
console.log(JSON.stringify(obj)); // {"a":1}
数组元素中,这些值会变成 null。
console.log(JSON.stringify([1, undefined, () => {}]));
// [1,null,null]
这就是接口数据里数组长度能保留,但对象字段可能消失的原因。
三、数字和 BigInt 的边界
JSON 数字不支持 NaN、Infinity、-Infinity,它们会变成 null。
JSON.stringify({ a: NaN, b: Infinity }); // {"a":null,"b":null}
BigInt 默认不能被 JSON 序列化。
JSON.stringify({ id: 1n }); // TypeError
如果后端 ID 可能超过安全整数,比如 9007199254740993,前端最好用字符串承接,避免精度丢失。
四、toJSON、replacer 和 space
对象可以定义 toJSON 控制序列化结果。
const user = {
name: "Ada",
password: "secret",
toJSON() {
return { name: this.name };
}
};
JSON.stringify(user); // {"name":"Ada"}
replacer 可以过滤或转换字段,space 用于格式化输出。
JSON.stringify({ a: 1, b: 2 }, ["a"], 2);
// {
// "a": 1
// }
五、parse 的 reviver 可以做恢复
JSON.parse 的第二个参数 reviver 会自底向上处理解析结果。
const result = JSON.parse('{"date":"2026-07-29"}', (key, value) => {
if (key === "date") return new Date(value);
return value;
});
这常用于把日期字符串恢复成 Date,或者对危险字段做过滤。
六、循环引用和深拷贝限制
JSON 方案不能处理循环引用,也会丢掉原型、方法、undefined、Map、Set 等信息。
object
├─ name: "node"
└─ self ──┐
▲ │
└────┘
这种结构序列化会抛 TypeError。如果要处理复杂对象,应考虑 structuredClone 或专门的序列化方案。
七、常见误区与追问
- 误区:JSON.stringify 可以安全深拷贝任何对象。 它会丢失函数、原型、特殊对象和循环引用。
- 误区:对象里的 undefined 会变成 null。 对象属性会被忽略,数组元素才会变成
null。 - 误区:Date 解析回来还是 Date。 默认回来是字符串,需要
reviver恢复。 - 误区:BigInt 会自动转成数字。 默认会抛错,不能直接序列化。
- 追问:replacer 和 reviver 分别在什么时候执行?
replacer在 stringify 阶段过滤转换,reviver在 parse 阶段恢复转换。 - 追问:接口大整数为什么常用字符串? 因为 JavaScript Number 有安全整数上限,超过后可能丢精度。
八、加强记忆
记忆路径:
JS value -> stringify -> JSON text -> parse -> JS value
中间这段 JSON text 只承载 JSON 类型。答题时抓三个点:对象和数组对特殊值处理不同;循环引用和 BigInt 会抛错;replacer、reviver、toJSON 可以定制转换。这样既能讲 API,也能讲限制。