JavaScript 的 JSON.stringify 和 JSON.parse 方法在序列化复杂对象时有何限制?

JavaScript 的 JSON.stringify 和 JSON.parse 方法在序列化复杂对象时有何限制?
最新回答
浅夏凉眸

2020-05-16 02:55:40

JavaScript 的 JSON.stringify 和 JSON.parse 在序列化复杂对象时存在四大核心限制,可能导致数据丢失或类型错误,具体如下

1. 无法处理循环引用
  • 问题:当对象属性直接或间接引用自身时,JSON.stringify 会抛出 TypeError: Converting circular structure to JSON。
  • 示例:const obj = { name: "test" };obj.self = obj;JSON.stringify(obj); // 抛出错误
  • 原因:JSON 标准不支持循环引用,序列化时会陷入无限递归。
  • 解决方案

    使用第三方库(如 flatted)或自定义 replacer 函数过滤循环引用。

    示例(使用 replacer):const getCircularReplacer = () => { const seen = new WeakSet(); return (key, value) => { if (typeof value === "object" && value !== null) { if (seen.has(value)) return "[Circular]"; seen.add(value); } return value; };};JSON.stringify(obj, getCircularReplacer()); // 输出:{"name":"test","self":"[Circular]"}

2. 特殊值被忽略或转换
  • 问题:JSON.stringify 对部分值的处理会导致信息丢失:

    忽略:undefined、function、Symbol(对象属性被忽略,数组元素转为 null)。

    转换:NaN 和 Infinity 转为 null;RegExp 变为空对象 {};Date 转为 ISO 格式字符串(但 JSON.parse 不会还原为 Date 实例)。

  • 示例:const data = { a: undefined, b: function() {}, c: /abc/, d: new Date(), e: NaN};JSON.stringify(data);// 输出:{"c":{},"d":"2024-01-01T00:00:00.000Z","e":null}
  • 影响:反序列化后无法恢复原始类型或行为(如 Date 需手动转换)。
3. 原型链和方法丢失
  • 问题:JSON.stringify 仅序列化对象的可枚举自有属性,不包含原型链上的属性或方法。反序列化后得到的是普通对象,失去原有类结构。
  • 示例:class Person { constructor(name) { this.name = name; } greet() { return `Hello, ${this.name}`; }}const p = new Person("Alice");const restored = JSON.parse(JSON.stringify(p));restored.greet(); // 报错:greet is not a function
  • 原因:JSON 不支持函数或原型链的序列化,导致反序列化后对象行为丢失。
4. 特殊对象无法正确序列化
  • 问题:Map、Set、TypedArray 等内置对象在序列化后数据或类型丢失:

    Map/Set 变为空对象 {}。

    TypedArray(如 Uint8Array)转为普通数组,丢失类型信息。

  • 示例:const map = new Map([["key", "value"]]);JSON.stringify(map); // 输出:"{}"const set = new Set([1, 2, 3]);JSON.stringify(set); // 输出:"{}"const arr = new Uint8Array([1, 2, 3]);JSON.stringify(arr); // 输出:"[1,2,3]"(类型丢失)
  • 影响:反序列化后无法恢复原始数据结构或功能。
替代方案建议
  • structuredClone(现代浏览器支持):

    支持循环引用、Date、RegExp、Map/Set 等,保留类型信息。

    示例:const obj = { a: new Date(), b: new Map([["key", "value"]]) };const cloned = structuredClone(obj); // 完整复制

  • 专用库

    flatted:处理循环引用。

    lodash.cloneDeep:深拷贝对象(支持部分特殊类型)。

  • 自定义序列化

    对特殊对象手动转换为可序列化格式(如 Map 转为数组 [...map])。

总结:JSON.stringify 和 JSON.parse 适用于简单数据交换,但处理复杂对象时需谨慎。对于需要保留类型、原型链或循环引用的场景,建议使用 structuredClone 或专用库。