JavaScript无万能深拷贝函数;JSON.parse(JSON.stringify())会丢失函数、undefined等且不支持循环引用;浅拷贝如{...obj}仅复制第一层,引用类型仍共享内存;手写深拷贝需递归+WeakMap防循环;生产环境推荐lodash.cloneDeep或structuredClone。
JavaScript 里没有“一键深拷贝”的万能函数,JSON.parse(JSON.stringify(obj)) 看似简单,但会丢函数、undefined、Symbol、循环引用、Date、RegExp 等;而浅拷贝用 Object.assign() 或展开运算符 {...obj} 就行,但只管第一层。
浅拷贝本质是让新对象和原对象的顶层属性指向相同的内存地址(对引用类型而言)。常见写法有:
Object.assign({}, obj) —— 注意第一个参数必须是空对象,否则会污染目标对象{...obj} —— 仅适用于对象字面量,不支持 class 实例或原型链上的属性Array.from(arr) 或 [...arr] —— 数组浅拷贝最常用,但嵌套数组仍共享子项引用典型陷阱:const a = { x: 1, y: { z: 2 } }; const b = { ...a }; b.y.z = 99; console.log(a.y.z); // 输出 99 —— 因为 y 是引用,没被真正复制。
手写深拷贝需判断类型、避开循环引用、处理特殊对象。一个最小可用版本如下:
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj);
const cloned = Array.isArray(obj) ? [] : {};
hash.set(obj, cloned);
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = deepClone(obj[key], hash);
}
}
return cloned;
}
关键点:
WeakMap 记录已克隆的对象,防止循环引用导致栈溢出Object.prototype.hasOwnProperty.call() 过滤原型链属性Date、RegExp、Map、Set 等内置类型,如需支持得额外分支判断JSON.parse(JSON.stringify(obj))?它看似简洁,但实际限制极多:
undefined、function、Symbol 字段会被直接忽略Date 变成字符串,RegExp 变成空对象 {}
TypeError: Converting circular structure to JSON
Object 实例仅适合临时处理“干净”的纯数据对象(比如 API 返回的扁平 JSON 数据)。
生产环境别自己造轮子,优先用成熟库:
_.cloneDeep() —— 兼容性好、覆盖类型全、性能经过优化structuredClone(obj) —— 原生支持 Date、Map、Set、ArrayBuffer 等,但暂不支持函数和 undefined
注意:structuredClone 在 Node.j

Error 对象和某些自定义类实例 —— 这些边界情况,往往才是深拷贝真正难搞的地方。