JSON.parse(JSON.stringify()) 不能处理循环引用,会报错 TypeError;structuredClone() 原生支持循环引用及多种类型但不支持函数等,兼容性需注意;手写 deepClone 需用 WeakMap 解决循环引用并适配各类原生对象。
这个方法会直接报错 TypeError: Converting circular structure to JSON,因为 JSON.stringify() 在序列化时遇到对象的某个属性指向自身(或形成闭环引用)就会中断。它不支持任何函数、undefined、Symbol、Date、RegExp、Map、Set 等非可序列化值,更不会记录已遍历过的对象。
核心思路是:在递归拷贝过程中,用 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() 是现代浏览器(Chrome 98+、Firefox 94+、Safari 16.4+)和 Node.js 18.13+ 原生提供的深拷贝 API,它原生支持循环引用、Map、Set、Date、RegExp、ArrayBuffer 等,但不支持函数、undefined、Symbol 和带有原型链的自定义类实例(只保留数据属性)。
deepClone 函数或引入 polyfill(如 core-js 提供的 structuredClone 模拟)--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 reactive() 不应被深拷贝,而应重新创建)循环引用只是深拷贝的显性门槛;真正难的是语义一致性——拷出来的对象是否在业务逻辑中“等价”。这点没有银弹,得看具体用在哪。