关键在于理解JavaScript内存模型:基本类型存栈中,赋值即复制值;引用类型存堆中,变量仅存地址,赋值复制地址。浅拷贝只新建第一层对象,嵌套仍共享;深拷贝则递归新建所有层级,完全隔离。

掌握浅拷贝与深拷贝,关键不是背方法,而是建立对 JavaScript 内存模型的直觉。一旦理解“变量存的是地址还是值”,所有拷贝行为就自然清晰了。
先盯住内存:栈和堆怎么分工
基本类型(string、number、boolean、null、undefined、symbol、bigint)存在栈里,赋值=真复制一份值;引用类型(Object、Array、Date、RegExp、Map、Set 等)本体存在堆里,变量里只存一个指向堆中位置的地址。
这就意味着:
– let a = {x: 1}; let b = a; → a 和 b 指向同一块堆内存,改 b.x 就等于改 a.x
– let c = 10; let d = c; → d 是 c 的独立副本,改 d 不影响 c
所谓“拷贝”,本质就是决定:新变量要不要在堆里新开一块地?开几层?
浅拷贝:只新建第一层,嵌套仍共用
它创建一个新对象/数组,但只复制顶层属性的值或引用。对基本类型是安全的,对嵌套对象或数组则完全不隔离。
常用方式包括:
– 对象:Object.assign({}, obj)、{...obj}
– 数组:[...arr]、arr.slice()、arr.concat()、Array.from(arr)
– 注意:Object.assign 不拷贝原型链属性和不可枚举属性;展开运算符 遇到 null/undefined 会报错
验证是否浅拷贝很简单:修改拷贝后对象的深层属性(如 copy.nested.value),看原对象对应字段是否同步变化。变了,就是浅的。
深拷贝:从根到叶,全量重建
它递归遍历所有层级,在堆中为每个嵌套对象/数组都分配新内存空间,确保两个对象彻底无关。
主流方案分三类:
– JSON 方法:JSON.parse(JSON.stringify(obj)),简单快,但丢函数、undefined、Symbol、Date(变字符串)、RegExp(变空对象)、Map/Set(丢失)、循环引用直接报错
– 现代原生 API:structuredClone(obj)(Chrome 98+/Firefox 94+/Safari 15.4+ 支持),能处理函数外的绝大多数类型,包括 Map、Set、Date、RegExp、Blob、TypedArray,也支持循环引用
– 手写或工具库:用 WeakMap 缓存已克隆对象防止循环引用,逐类型判断(Array、Date、RegExp、Map、Set…)并分别构造实例。Lodash 的 cloneDeep 就是成熟实现
实战中怎么选:看场景,不炫技
– 表单临时编辑、React state 更新、Redux reducer 处理简单配置 → 浅拷贝够用,性能好
– 后端返回的复杂嵌套数据需本地持久化、多模块共享又各自可修改 → 必须深拷贝
– 项目已支持 structuredClone 且无需兼容老浏览器 → 优先用它,最稳最简
– 需要兼容 IE 或必须保留函数/Symbol → 用 Lodash 或自研带类型判断的递归方案
– 临时调试或数据结构极简单 → JSON 方案快速验证逻辑,但别上生产
真正熟练,是你看到一行 const copy = {...data} 时,能立刻脑内模拟出内存里几个对象、几条引用线,并预判哪一层改了会影响谁。











