JSON.parse(JSON.stringify()) 不能处理循环引用,会报错 TypeError;structuredClone() 原生支持循环引用及多种类型但不支持函数等,兼容性需注意;手写 deepClone 需用 WeakMap 解决循环引用并适配各类原生对象。

JSON.parse(JSON.stringify()) 为什么不能处理循环引用
这个方法会直接报错 TypeError: Converting circular structure to JSON,因为 JSON.stringify() 在序列化时遇到对象的某个属性指向自身(或形成闭环引用)就会中断。它不支持任何函数、undefined、Symbol、Date、RegExp、Map、Set 等非可序列化值,更不会记录已遍历过的对象。
用 WeakMap 记录已拷贝对象来解决循环引用
核心思路是:在递归拷贝过程中,用 WeakMap 缓存原始对象 → 拷贝后对象的映射。每次进入新对象前先查表,命中就直接返回对应拷贝体,避免无限递归。
适用场景:需要保留函数、Date、RegExp、Map、Set 等原生类型,且必须支持任意嵌套与循环引用。
function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
let cloned;
if (obj instanceof Date) {
cloned = new Date(obj.getTime());
} else if (obj instanceof RegExp) {
cloned = new RegExp(obj);
} else if (obj instanceof Map) {
cloned = new Map();
seen.set(obj, cloned);
obj.forEach((value, key) => cloned.set(deepClone(key, seen), deepClone(value, seen)));
} else if (obj instanceof Set) {
cloned = new Set();
seen.set(obj, cloned);
obj.forEach(value => cloned.add(deepClone(value, seen)));
} else {
cloned = Array.isArray(obj) ? [] : {};
seen.set(obj, cloned);
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = deepClone(obj[key], seen);
}
}
}
return cloned;
}
structuredClone() 是什么,能替代手写吗
structuredClone() 是现代浏览器(Chrome 98+、Firefox 94+、Safari 16.4+)和 Node.js 18.13+ 原生提供的深拷贝 API,它原生支持循环引用、Map、Set、Date、RegExp、ArrayBuffer 等,但不支持函数、undefined、Symbol 和带有原型链的自定义类实例(只保留数据属性)。
立即学习“Java免费学习笔记(深入)”;
- 能用就优先用,性能好、无 bug 风险
- 注意兼容性:旧环境需降级到
deepClone函数或引入 polyfill(如core-js提供的structuredClone模拟) - Node.js 中需启用
--enable-structured-clone标志(v18.13–v20.0),v20.1+ 默认开启
哪些情况会让深拷贝“看似成功”却出问题
最容易被忽略的是原型链丢失和不可枚举属性处理:
-
Object.create(null)创建的对象没有constructor和__proto__,但多数手写 clone 会误当作普通 plain object 处理,导致后续instanceof判定失效 - 使用
for...in或Object.keys()遍历时,会跳过不可枚举属性(如Object.defineProperty(obj, 'x', { value: 1, enumerable: false })) - 某些库(如 Vue 的响应式对象、JQuery 封装对象)内部有特殊属性或方法,盲目深拷贝可能破坏其行为,应优先使用其官方克隆方法(如
Vue reactive()不应被深拷贝,而应重新创建)
循环引用只是深拷贝的显性门槛;真正难的是语义一致性——拷出来的对象是否在业务逻辑中“等价”。这点没有银弹,得看具体用在哪。











