JSON序列化
JSON 序列化的行为特征、数据丢失点和常见问题。
#status / growing
#type / concept
[!info] related notes
- 相关笔记: structured-clone, 深拷贝, 浅拷贝 vs 深拷贝
JSON 序列化
一句话定义
JSON.stringify() 将 JavaScript 值转换为 JSON 字符串,JSON.parse() 将 JSON 字符串还原为 JavaScript 值。它是跨语言数据交换的基础手段。
核心代码示例
// stringify
const obj = { name: 'Alice', age: 25, active: true };
const str = JSON.stringify(obj);
// '{"name":"Alice","age":25,"active":true}'
// parse
const restored = JSON.parse(str);
// { name: 'Alice', age: 25, active: true }
// 嵌套
const nested = { user: { name: 'Bob', scores: [90, 85] } };
JSON.stringify(nested);
// '{"user":{"name":"Bob","scores":[90,85]}}'
toJSON 方法
对象可以定义 toJSON() 方法来自定义序列化行为:
const user = {
name: 'Alice',
password: 'secret123',
toJSON() {
return { name: this.name }; // 不暴露密码
}
};
JSON.stringify(user); // '{"name":"Alice"}'
replacer 和 reviver
// replacer:过滤/转换输出
const data = { name: 'Alice', password: 'secret', age: 25 };
// 白名单方式
JSON.stringify(data, ['name', 'age']);
// '{"name":"Alice","age":25}'
// 函数方式
JSON.stringify(data, (key, value) => {
if (key === 'password') return undefined; // 过滤
return value;
});
// reviver:转换解析结果
const text = '{"name":"Alice","date":"2024-01-01T00:00:00.000Z"}';
const parsed = JSON.parse(text, (key, value) => {
if (key === 'date') return new Date(value);
return value;
});
// { name: 'Alice', date: Date实例 }
数据丢失点
const problematic = {
undef: undefined, // 对象中:键被忽略
fn: () => {}, // 对象中:键被忽略
sym: Symbol('x'), // 对象中:键被忽略
date: new Date(), // 变为 ISO 字符串(不再是 Date)
regex: /test/g, // 变为 {}
map: new Map(), // 变为 {}
set: new Set(), // 变为 {}
nan: NaN, // 变为 null
inf: Infinity, // 变为 null
};
JSON.stringify(problematic);
// '{"date":"2026-05-27T...","regex":{},"map":{},"set":{},"nan":null,"inf":null}'
// 数组中的特殊情况
JSON.stringify([undefined, () => {}, Symbol('x')]);
// '[null,null,null]' — 不是忽略,而是替换为 null
循环引用
const circular = { name: 'Alice' };
circular.self = circular;
JSON.stringify(circular);
// TypeError: Converting circular structure to JSON
解决方案:
// 方法一:replacer + WeakMap
function safeStringify(obj) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) return '[Circular]';
seen.add(value);
}
return value;
});
}
BigInt 边界
JSON.stringify({ n: 123n });
// TypeError: Do not know how to serialize a BigInt
// 解决:自定义 replacer
JSON.stringify({ n: 123n }, (key, value) =>
typeof value === 'bigint' ? value.toString() : value
);
// '{"n":"123"}'
性能
JSON.parse/JSON.stringify底层由 C++ 引擎实现,对纯数据对象非常快。- 比
structuredClone快 2-5 倍(纯数据场景)。 - 大对象场景:先
stringify再parse仍然是最常用的深拷贝手段。
Web 场景为什么没问题
在 Web 后端(如 Node.js 接口),你 return 一个带有方法或 Getter 的 ORM 模型实例,HTTP 框架底层会调用 JSON 序列化。方法被静默丢弃了,前端刚好只拿到了纯数据,大家”相安无事”。