JSON.parse(JSON.stringify) 深拷贝丢什么:undefined、Date、RegExp、循环引用四个坑

JSON.parse(JSON.stringify) 会悄悄丢掉 undefined 字段和函数,把 Date 变成字符串、RegExp 变成空对象,遇到循环引用直接抛 TypeError。现代浏览器请直接用 structuredClone,本文逐项对照两种方式的差异并给出选型建议。

约 6 分钟读完 · 更新于 2026-10-21 · 文中每条都能在下面的配套工具里动手验证

结论:现代浏览器直接 structuredClone

JSON.parse(JSON.stringify) 只适合「纯数据」对象:字符串、数字、布尔、普通数组。字段里出现 Date、undefined、函数、Map 中的任何一种,结果就不对了。

四个坑现场演示

跑一遍就知道,丢失全是静默发生的,控制台不报任何错。

JSON 方式深拷贝
const src = {
  name: '张三',
  created: new Date('2026-01-01'),
  pattern: /a+b/g,
  note: undefined,
  say() { return this.name; },
};
const copied = JSON.parse(JSON.stringify(src));
console.log(copied);
// {
//   name: '张三',
//   created: '2026-01-01T00:00:00.000Z',  ← Date 变字符串
//   pattern: {},                          ← RegExp 变空对象
// }                                       ← note 和 say 被整个丢掉

逐项对照:JSON 方式 vs structuredClone

同一份输入,两种方式的输出差异一览。

输入值JSON 方式结果structuredClone 结果
undefined 字段字段被丢掉原样保留
new Date()变成 ISO 字符串Date 实例
/a+b/g变成空对象 {}RegExp 实例
NaN / Infinity变成 null原样保留
循环引用抛 TypeError正常克隆
Map / Set变成 {}正常克隆
函数被丢掉抛 DataCloneError

structuredClone 用法与边界

推荐写法
const src = {
  created: new Date('2026-01-01'),
  pattern: /a+b/g,
  nested: { deep: [1, [2, 3]] },
  tags: new Set(['a', 'b']),
};
src.self = src; // 循环引用:JSON 方式在这里直接抛错

const cloned = structuredClone(src);
console.log(cloned.created instanceof Date); // true
console.log(cloned.self === cloned);         // true,循环结构完整保留

遇到 Converting circular structure to JSON 怎么排查

这是 JSON 深拷贝最常见的报错,但报错栈通常不会指明是哪个字段引起的。

  1. 确认场景:报错发生在 JSON.stringify 或 axios 序列化时,说明数据里有对象自己引用了自己。
  2. 打印怀疑对象:console.dir(data, { depth: 4 }),看哪一层出现了 [Circular] 标记。
  3. 临时绕过:只序列化需要的字段,别整棵树一起 stringify。
  4. 根治:克隆场景换成 structuredClone;纯序列化场景给 stringify 传第二个参数,剔除循环字段。

浅拷贝陷阱:Object.assign 和展开运算符

它们都只复制第一层,嵌套对象仍共享引用——改副本的嵌套字段,原对象跟着变。

一行看懂浅拷贝
const src = { name: '张三', addr: { city: '杭州' } };
const copy = { ...src };
copy.addr.city = '上海';
console.log(src.addr.city); // '上海' ← 原对象也被改了

选型建议

不是所有场景都需要真深拷贝,按数据形态选。

  • 纯数据、无函数、无 Date 语义需求:JSON 方式仍可用,但 structuredClone 更省心,没有理由不换。
  • 需要拷贝类实例并保留方法与原型:structuredClone 会丢原型,此时手写递归或用 lodash.cloneDeep。
  • 数据来自接口、只想「防误改」:考虑 Object.freeze 只读快照,比拷贝更便宜。
  • 怀疑拷贝丢字段:把源对象和拷贝对象同时粘进 JSON 格式化工具 diff 一下,丢失字段一眼可见。

常见问题

structuredClone 兼容性怎么样,能直接用吗?

2022 年起 Chrome、Firefox、Safari、Edge 全部支持,Node 17+ 也可用,可以放心上生产。只有需要兼容 IE 时才退回手写递归或 lodash。

JSON 方式深拷贝为什么遇到循环引用就报错?

JSON 序列化要求结构是严格的树,循环引用会无限递归,所以直接抛 TypeError。structuredClone 基于结构化克隆算法,循环结构原样保留。

structuredClone 能拷贝函数吗?

不能,函数会抛 DataCloneError,DOM 节点、Symbol 同样不支持。普通对象克隆后原型会丢失变成普通对象,自定义类实例要注意。

浅拷贝和深拷贝的区别一句话是什么?

浅拷贝只复制第一层,嵌套的引用类型仍指向原对象;深拷贝递归复制所有层级。Object.assign 和展开运算符都是浅拷贝,嵌套对象会互相影响。

📂

松开以自动格式化

支持 .json / .sql / .csv / .html / .css / .xml / .yaml 文件