JavaScript中浅拷贝只复制第一层属性引用,深拷贝需递归处理所有层级;JSON方法不可靠,手写需防循环引用和类型陷阱,生产环境推荐_.cloneDeep或structuredClone。

JavaScript 中的浅拷贝只复制对象第一层属性的引用,深拷贝则递归复制所有嵌套层级,确保新旧对象完全独立。直接用 JSON.parse(JSON.stringify(obj)) 看似简单,但会丢函数、undefined、Symbol、Date、RegExp、循环引用等,不能算真正可靠的深拷贝。
浅拷贝的典型表现和常见写法
浅拷贝后,修改嵌套对象的属性会影响原对象。比如:
const a = { x: 1, y: { z: 2 } };
const b = { ...a }; // 或 Object.assign({}, a)
b.y.z = 999;
console.log(a.y.z); // 输出 999 —— 被意外改了
常见浅拷贝方式包括:Object.assign()、扩展运算符 {...obj}、Array.prototype.slice()、Array.from()。它们都只处理一层,对嵌套对象/数组仍保留引用。
手写递归深拷贝要注意的关键点
自己实现时需覆盖基本类型、null、数组、普通对象,并跳过不可枚举或原型链上的属性。还要避免循环引用导致栈溢出。
立即学习“Java免费学习笔记(深入)”;
- 用
Map缓存已拷贝的对象,遇到重复引用直接返回缓存结果 -
typeof null === 'object'是个陷阱,得先用val === null单独判断 -
Array.isArray(val)比val.constructor === Array更可靠 - 不处理
Date、RegExp等内置对象时,它们会被当成普通对象拷贝,丢失类型和方法
function deepClone(obj, map = new Map()) {
if (obj === null || typeof obj !== 'object') return obj;
if (map.has(obj)) return map.get(obj);
const cloned = Array.isArray(obj) ? [] : {};
map.set(obj, cloned);
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = deepClone(obj[key], map);
}
}
return cloned;
}
生产环境建议用成熟方案而非手写
手写容易漏边缘 case,尤其涉及 Map、Set、TypedArray、Promise 等时。Lodash 的 _.cloneDeep() 经过大量测试,支持多数内置类型;如果项目已用 structuredClone()(Chrome 98+、Node.js 17.0+),它原生支持 Date、RegExp、ArrayBuffer 等,且自动处理循环引用,但不支持函数和 undefined。
-
structuredClone(obj)最快,但兼容性有限,且会抛错:「DataCloneError」当遇到函数或某些不可序列化值 -
_.cloneDeep(obj)兼容性好,但体积大,若只用深拷贝可考虑轻量替代如klona - 不要在性能敏感路径(如 render 循环)中调用深拷贝,尤其对象很深或很大时
真正难的不是“怎么写一个能跑的”,而是“怎么覆盖所有 JS 值类型 + 不拖慢运行时 + 不爆栈”。选轮子前,先看清楚你的数据里有没有 function、undefined、BigInt 或跨 iframe 的对象——这些都会让多数通用方案失效。











