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 是引用,没被真正复制。
深拷贝:递归复制所有层级的可遍历属性
手写深拷贝需判断类型、避开循环引用、处理特殊对象。一个最小可用版本如下:
立即学习“Java免费学习笔记(深入)”;
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 数据)。
现代项目中更推荐的方案
生产环境别自己造轮子,优先用成熟库:
- Lodash 的
_.cloneDeep()—— 兼容性好、覆盖类型全、性能经过优化 - 结构化克隆(Chrome 98+ / Firefox 94+):
structuredClone(obj)—— 原生支持Date、Map、Set、ArrayBuffer等,但暂不支持函数和undefined
注意:structuredClone 在 Node.js 17.0+ 才可用,且不支持 Error 对象和某些自定义类实例 —— 这些边界情况,往往才是深拷贝真正难搞的地方。











