JavaScript对象拷贝分浅拷贝和深拷贝:浅拷贝仅断开第一层引用,嵌套对象仍共享内存;深拷贝需递归创建所有层级独立副本,处理类型识别、特殊值及循环引用。

JavaScript 对象拷贝的核心在于引用是否被切断——浅拷贝只断开第一层,深拷贝要逐层新建内存空间。
浅拷贝:只复制顶层,嵌套仍共享
浅拷贝创建新对象,但对引用类型(如对象、数组)只复制其内存地址,不生成新实例。修改嵌套内容时,原对象同步变化。
-
展开运算符
{...obj}:简洁,但仅适用于对象字面量;对null或undefined会报错 -
Object.assign({}, obj):拷贝可枚举自有属性,不处理原型链、Symbol键、不可枚举属性 -
Array.prototype.slice()或[...arr]:适合数组,但若数组含对象,内部仍是浅拷贝
例如:const a = { x: 1, y: { z: 2 } }; const b = { ...a }; b.y.z = 99; → a.y.z 也变成 99。
深拷贝:递归隔离所有层级
深拷贝要求每个嵌套对象、数组、日期、正则等都创建独立副本,确保新旧对象彻底无关。关键挑战是类型识别、特殊值处理和循环引用。
立即学习“Java免费学习笔记(深入)”;
-
structuredClone():现代标准方案(Chrome 98+、Firefox 94+、Safari 15.4+、Node.js 17.0+),原生支持Date、RegExp、Map、Set、ArrayBuffer和循环引用,但不支持function、undefined、Symbol -
JSON.parse(JSON.stringify(obj)):快但限制明显——丢function、undefined、Symbol、Date(转字符串)、RegExp(变空对象),且遇循环引用直接报错 -
手写递归 +
WeakMap:需判断类型(Object.prototype.toString.call()或Array.isArray()),区分基本类型/内置对象/普通引用;用WeakMap缓存已拷贝对象,避免循环引用导致栈溢出
选型建议:按环境与需求匹配
没有万能解法,关键是看运行环境和数据结构复杂度:
- 现代环境(支持
structuredClone)→ 优先用它,最可靠 - 纯 JSON 数据 + 无需兼容老浏览器 →
JSON.parse(JSON.stringify())足够快 - 需支持
function、undefined或老环境(如 IE)→ 引入lodash.cloneDeep等成熟库 - 有定制需求(如忽略某些字段、转换特定类型)→ 手写递归,但务必处理循环引用和边界类型
容易忽略的关键细节
很多“翻车”源于没意识到这些隐性行为:
-
Object.assign()和展开运算符都不拷贝原型链上的属性 -
JSON.stringify()会跳过undefined值和Symbol键,导致数据丢失 - 循环引用不是罕见边缘情况——DOM 节点、事件监听器、状态管理中的依赖图都可能隐含闭环
-
structuredClone()抛出DataCloneError时,说明遇到了无法克隆的值(如函数),需提前过滤或降级处理


















