浅拷贝只复制对象第一层属性,嵌套引用仍共享;深拷贝递归复制所有层级,彻底隔离堆内存数据。常见浅拷贝方法有展开运算符、object.assign()、slice()等;深拷贝推荐structuredclone()或json.parse(json.stringify())(注意其限制)。

JavaScript中处理引用类型赋值时,浅拷贝和深拷贝的核心区别在于:是否真正分离堆内存中的对象数据。直接赋值只是复制引用地址,修改原对象会影响副本;浅拷贝只断开第一层引用,嵌套对象仍共享;深拷贝则递归复制所有层级,彻底隔离。
浅拷贝:只复制顶层属性,嵌套引用仍共用
适用于对象或数组仅有一层结构,或你明确希望内层保持联动的场景。常见方法有:
-
展开运算符(...):
const copy = {...obj}或[...arr],简洁但只作用于第一层 -
Object.assign():
const copy = Object.assign({}, obj),同样不处理嵌套对象 -
Array.prototype.slice() / concat():仅对数组有效,如
arr.slice()
注意:如果原对象含 Date、RegExp、Map、Set 或深层嵌套对象,这些方法无法正确拷贝其内部状态或结构。
深拷贝:完全独立副本,修改互不影响
当需要彻底隔离数据(如表单初始值缓存、状态快照、配置备份),必须使用深拷贝。推荐方式:
- JSON.parse(JSON.stringify()):最简方案,但会丢失函数、undefined、Symbol、Date 对象(转成字符串)、正则、循环引用,且不能处理 BigInt
-
structuredClone()(现代浏览器支持):
const deepCopy = structuredClone(obj),支持 Map、Set、Date、RegExp、ArrayBuffer 及嵌套结构,是目前最可靠的标准方案 - 第三方库(如 lodash.cloneDeep):兼容性好,处理边界情况更全面,适合需要支持老旧环境的项目
避免手写递归深拷贝——容易遗漏类型判断、陷入循环引用、忽略原型链或不可枚举属性。
如何判断是否需要深拷贝?看数据用途和修改预期
不是所有引用赋值都需要深拷贝。关键看后续操作:
- 仅读取或临时计算 → 直接赋值即可
- 修改副本但不想影响原始数据 → 检查结构深度:单层用浅拷贝,多层或不确定用深拷贝
- 涉及异步更新、React/Vue 的响应式状态、或跨模块传递配置 → 建议默认深拷贝,避免隐性副作用
例如 Vue 中给 ref 赋值一个对象时,若后续会在组件内修改它,又不希望影响源头,就必须深拷贝。
特殊场景:冻结对象与不可变更新
若原始对象已被 Object.freeze() 或处于不可变状态,浅拷贝后仍无法修改——此时深拷贝也无意义。更合理的做法是结合不可变更新模式:
- 用
{...obj, key: newValue}替代修改原对象 - 数组用
[...arr.slice(0, i), newItem, ...arr.slice(i + 1)]实现“更新” - 配合 immer 等库,在不手动深拷贝的前提下写出可读性强的不可变逻辑
这种思路比盲目深拷贝更高效,也更符合现代前端开发习惯。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











