javascript对象拷贝本质是处理引用是否切断:基本类型栈中存值,天然独立;引用类型栈存地址、堆存数据,直接赋值即共享堆内存。浅拷贝仅断第一层引用,快但嵌套仍共享;深拷贝递归重建所有堆空间,彻底隔离但需防循环引用等坑。

JavaScript对象拷贝的核心逻辑,本质是处理「引用是否被切断」——基本类型天然独立,而引用类型默认共享堆内存。设计思维围绕一个根本目标展开:在数据隔离与性能开销之间做权衡,根据使用场景选择“断到哪一层”。
为什么必须区分深浅?根源在内存模型
基本类型(String、Number等)存在栈中,赋值即复制值;引用类型(Object、Array等)的变量只存地址,实际数据在堆里。所以直接赋值 = 复制地址,两个变量指向同一块堆空间。浅拷贝只新建第一层对象容器,内部嵌套的引用仍共用;深拷贝则递归新建每一层堆空间,彻底物理隔离。
浅拷贝:只断第一层引用,快但有边界
适用场景:对象结构扁平、确认内部无嵌套引用,或仅需临时隔离顶层属性。
- 展开运算符 {...obj}:简洁直观,仅处理可枚举自有属性
- Object.assign({}, obj):支持多源合并,但不拷贝原型链和不可枚举属性
- 数组方法 slice()/concat()/toReversed().reverse():对数组有效,但元素为对象时,那些对象仍被共享
⚠️ 共同缺陷:遇到 { a: 1, b: { c: 2 } } 这类结构,修改 copy.b.c 依然会影响原对象。
深拷贝:逐层重建堆空间,完整独立但需防坑
目标是让新对象与原对象在内存中完全无关,但实现方式决定可靠性与兼容性。
- structuredClone():现代标准方案(Chrome 98+、Node.js 17.0+),原生支持 Map、Set、Date、RegExp、ArrayBuffer 和循环引用,无需手动处理边界情况
- JSON.parse(JSON.stringify(obj)):快但脆弱,会丢弃 function、undefined、Symbol、BigInt,把 Date 变字符串,无法处理循环引用
-
手写递归:可控性强,需用
Object.prototype.toString.call()判断类型,用WeakMap缓存已处理对象以解决循环引用,还要单独处理 Date、RegExp 等内置对象
设计思维的关键落点:按需选择,不盲目求“深”
不是所有场景都需要深拷贝。例如表单临时编辑、配置合并、状态快照,浅拷贝更轻量;而涉及嵌套结构变更、历史版本保存、跨组件数据传递,则必须深拷贝。真正的设计意识在于:看清数据结构层级、识别是否存在副作用风险、评估运行环境支持能力,再匹配最简够用的方案。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











