JavaScript中无自动深拷贝,赋值默认为浅拷贝;浅拷贝仅复制顶层属性,引用类型仍共享内存;JSON方法有诸多限制;可靠方案是structuredClone()或lodash.cloneDeep()。

JavaScript 中没有“自动深拷贝”,所有对象赋值默认都是浅拷贝;想避免引用问题,必须显式选择合适方式处理嵌套结构。
什么是浅拷贝?Object.assign() 和展开运算符 {...obj} 都只拷贝第一层
浅拷贝只复制对象的顶层属性值。如果属性是引用类型(比如数组、对象、Date、RegExp),拷贝后新旧对象仍共享该引用。
常见错误现象:obj2.name = "new" 不影响 obj1.name,但 obj2.items.push(1) 会同步出现在 obj1.items 中。
-
Object.assign({}, obj):不处理原型链、不拷贝 symbol 属性、忽略不可枚举属性 -
{...obj}:同上,且无法处理函数、undefined、null 作为属性值时的行为差异 - 两者都无法处理循环引用,遇到会报错或静默失败
JSON 方法做深拷贝的限制:JSON.parse(JSON.stringify(obj)) 看似简单,实则脆弱
这是最常被误用的“深拷贝”方案,但它在多种场景下直接失效:
立即学习“Java免费学习笔记(深入)”;
- 函数、
undefined、Symbol、BigInt会被丢弃 -
Date变成字符串,RegExp变成空对象{} - 循环引用会抛出
TypeError: Converting circular structure to JSON - Map、Set、TypedArray、Error 对象等原生类型无法序列化
仅适用于纯数据对象(POJO),且不含上述任何特殊值——实际业务中极少满足。
真正可靠的深拷贝:用 structuredClone()(现代浏览器)或 lodash.cloneDeep()(兼容性要求高时)
structuredClone() 是原生 API,支持 Map、Set、Date、RegExp、ArrayBuffer、TypedArray、Error、Promise(克隆为 pending 状态)等,并能正确处理循环引用。
- 支持条件:
Chrome 98+、Firefox 94+、Safari 15.4+;Node.js 17.0+(需启用--experimental-structured-cloning,18.10+ 默认开启) - 不支持函数、
undefined、Symbol、BigInt—— 这是设计使然,不是 bug - 示例:
const deepCopy = structuredClone(originalObj);
若需支持更老环境或函数/undefined 等值,lodash.cloneDeep() 更稳妥,但它会把 Date、RegExp 等转为普通对象,行为与 structuredClone() 不同,需按需选型。
如何判断是否真需要深拷贝?多数时候你其实只需要“不可变更新”
深拷贝开销大,且容易掩盖设计问题。很多场景下,用函数式更新 + 解构 + 扩展运算符更轻量、更可控:
- 更新嵌套字段:
{ ...state, user: { ...state.user, name: "Alice" } } - 更新数组某项:
[...items.slice(0, i), newItem, ...items.slice(i + 1)] - 配合 React、Redux、Immer 使用时,深拷贝反而是反模式
真正该警惕的,是那些你以为“只是读取”却意外修改了原始对象的地方——比如把一个 props 对象直接传给第三方库并让它 mutate,或者在 reducer 里用了 push() / sort() 这类原地方法。











