JavaScript深拷贝无万能方案:structuredClone()支持多数内置类型且处理循环引用,但不支持function等;手写需防null、数组遍历、循环引用等陷阱;Lodash cloneDeep覆盖广但有体积和兼容性考量;JSON方法仅适用于纯数据对象。
JavaScript 深拷贝没有“银弹”,JSON.parse(JSON.stringify(obj)) 看似简单,但会丢函数、undefined、Symbol、Date、RegExp、Map、Set、循环引用等;真正可靠的方式得按场景选。
structuredClone() —— 现代浏览器首选这是目前最接近“开箱即用”的原生深拷贝方案,支持大多数内置类型(包括 Date、RegExp、Map、Set、ArrayBuffer、TypedArray),且能处理循环引用。
注意点:
--experimental-structured-cloning 标志function、undefined、Symbol、Promise、Error 实例(会抛错或跳过)prototype 方法的自定义类实例(只保留数据属性)const obj = { a: 1, b: new Date(), c: n
ew Map([[1, 'x']]) };
const cloned = structuredClone(obj); // ✅ 安全、快、语义清晰
当你要保留 function、undefined、Symbol,或需对特定字段做定制处理(如忽略某些 key、转换日期格式),就得自己实现。关键不是“写全所有类型”,而是明确你的数据契约。
常见陷阱:
null(它 typeof 是 "object",但不是对象)for...in 遍历数组,导致索引变成字符串、丢失稀疏性Object.prototype.toString.call() 或 Array.isArray() 判数组,误把类数组当普通对象function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
const 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;
}
cloneDeep() 最常用它覆盖了绝大多数边界情况:Buffer、Promise、HTML 元素、arguments、带 constructor 的对象等。但要注意体积和 tree-shaking 支持。
使用前确认:
structuredClone 更轻量lodash.clonedeep 单独安装)eval 或 Function 构造器,而 Lodash 内部有动态代码生成逻辑(极少数情况)import cloneDeep from 'lodash.clonedeep';
const data = { fn: () => {}, date: new Date() };
const copied = cloneDeep(data); // ✅ fn 被保留为 function,date 正确复制
JSON.parse(JSON.stringify())?它只适合“纯数据对象”——即键值全是字符串、数字、布尔、null、普通数组或嵌套对象。一旦出现以下任一,结果就不可靠:
undefined 和 function 字段直接被忽略Symbol 键被丢弃Date 变成字符串("2025-05-20T12:00:00.000Z"),不再是 Date 实例NaN、Infinity 变成 null
TypeError: Converting circular structure to JSON
const bad = { a: undefined, b: () => {}, c: Symbol('x'), d: new Date() };
console.log(JSON.parse(JSON.stringify(bad))); // { d: "2025-05-20T12:00:00.000Z" } —— 其他全没了
深拷贝真正的复杂点不在“怎么写”,而在“你到底想拷什么”。函数要不要?原型链要不要?特殊对象(如 canvas 上下文)要不要?先画清数据边界,再选工具,比背方法更重要。