浅拷贝只复制第一层属性,对引用类型仅复制地址,导致嵌套修改影响原对象;JSON.parse(JSON.stringify())有诸多限制,不适用于复杂类型;推荐structuredClone()或lodash.cloneDeep(),但应优先优化设计减少深拷贝需求。

浅拷贝只复制引用,对象嵌套时会出问题
JavaScript 中的 Object.assign()、扩展运算符 {...obj}、Array.prototype.slice() 都是浅拷贝。它们只复制第一层属性的值:对基本类型(如 number、string)是真复制;但对对象、数组等引用类型,只是把内存地址复制了一份。
后果就是:修改拷贝后的嵌套对象,原对象也会被改掉。
const a = { user: { name: 'Alice' } };
const b = { ...a };
b.user.name = 'Bob';
console.log(a.user.name); // 'Bob' —— 原对象被意外修改
常见踩坑场景包括:React 组件中直接解构 props 后修改 state 里的嵌套对象、Vuex mutation 中误改 payload 深层结构、表单编辑时未隔离原始数据。
JSON.parse(JSON.stringify()) 不是通用深拷贝方案
这个“取巧”写法看似能深拷贝,但有多个硬伤,实际项目中容易引发静默错误:
立即学习“Java免费学习笔记(深入)”;
-
undefined、function、Symbol、BigInt会被直接丢弃 - 循环引用会抛出
TypeError: Converting circular structure to JSON - Date、RegExp、Map、Set、TypedArray 等内置对象会变成空对象或字符串(如
new Date()变成 ISO 字符串,再 parse 回来就只是 string) - 原型链和 constructor 信息全部丢失
它只适合纯数据对象(POJO)且确定不含上述类型的简单场景,比如临时处理 API 返回的扁平配置项。
推荐用 structuredClone() 或第三方库应对真实需求
现代浏览器(Chrome 98+、Firefox 94+、Safari 15.4+)已支持原生 structuredClone(),它能正确处理 Date、Map、Set、ArrayBuffer、TypedArray 和嵌套对象,也支持循环引用(前提是可序列化),且保留类型语义。
const obj = { date: new Date(), map: new Map([['key', 'value']]) };
const cloned = structuredClone(obj);
console.log(cloned.date instanceof Date); // true
console.log(cloned.map instanceof Map); // true
兼容性要求高时,可选用 lodash.cloneDeep()(注意它也不能深拷贝函数和 DOM 节点)或 fast-deep-cloning(更轻量,但不支持 Map/Set)。避免自己手写递归深拷贝——边界情况太多,比如 Proxy、WeakMap、error 实例等几乎无法安全处理。
深拷贝不是银弹,多数时候该换思路
频繁深拷贝往往暴露了设计问题:比如状态更新逻辑耦合太紧、不可变更新没落实、或过度防御式编程。React 的 useReducer + immer、Redux Toolkit 的 createSlice、Vue 的响应式 proxy 机制,本质都是通过代理或结构共享减少拷贝开销。
真正需要深拷贝的典型场景其实很窄:导出快照、跨 worker 传递数据、测试中隔离 fixture、或对接不接受引用变更的外部 SDK。其他时候,优先考虑 immer 这类库提供的“看起来可变、底层不可变”的写法,比手动深拷贝更可靠、更易维护。
别为了“安全”无脑深拷贝——性能损耗、内存泄漏、类型丢失都可能在后期才暴露,而且很难定位。










