JavaScript拷贝对象无官方规范,但有明确语义:浅拷贝仅分离第一层属性,嵌套引用仍共享;深拷贝则递归隔离所有层级,需处理循环引用与特殊类型;实际选择依场景而定。

JavaScript 中拷贝对象没有统一的“官方规范”,但存在明确的语义定义、行为共识和事实标准,由语言特性、ECMAScript 规范及开发者实践共同确立。
核心定义很清晰:
拷贝对象,是指基于一个已有引用类型(Object、Array 等)创建一个新对象,使两者在内存中独立存在;而“是否独立”取决于嵌套层级——这直接区分了浅拷贝与深拷贝。
拷贝的本质前提:只对引用类型有意义
基本类型(string、number、boolean、null、undefined、symbol、bigint)赋值即拷贝值,天然“深”。
只有引用类型才涉及地址共享问题,因此所有关于“拷贝”的讨论,默认针对 object 及其子类型(数组、日期、正则、Map、Set 等)。
浅拷贝的定义与判定标准
新对象与原对象第一层属性完全分离,但任意一层中的引用类型值仍共享同一内存地址。
满足以下任一条件即为浅拷贝:
- 修改新对象的基本类型属性,不影响原对象 ✅
- 修改新对象某属性(如
obj.a.b = 10)中的嵌套对象/数组,原对象对应位置同步变化 ❌(说明未深)
常用方法包括:
-
Object.assign({}, obj) -
{...obj}(扩展运算符) -
Array.prototype.slice()/[...arr]/arr.concat() -
Array.from(arr)
⚠️ 注意:这些方法均不处理原型链属性、不可枚举属性、Symbol 键,也不递归处理嵌套对象。
立即学习“Java免费学习笔记(深入)”;
深拷贝的定义与判定标准
新对象与原对象所有层级的属性都完全独立,修改任意嵌套结构,双方互不影响。
理想深拷贝需同时满足:
- 基本类型值被复制 ✅
- 所有嵌套对象、数组、Date、RegExp、Map、Set 等均被递归重建 ✅
- 循环引用能被识别并正确映射(避免栈溢出或无限递归)✅
- 特殊值如
undefined、function、Symbol、BigInt能按需保留或合理降级 ✅
现实中无万能方案,常见实现方式有:
-
JSON.parse(JSON.stringify(obj)):简单快捷,但丢失函数、undefined、Symbol、Date、RegExp、循环引用等 - 递归遍历 + 类型判断手写函数:可控性强,可定制,但需自行处理循环引用(通常用
WeakMap缓存已拷贝对象) - 使用成熟库(如 Lodash 的
_.cloneDeep):覆盖全面,经过大量边界测试
实际开发中的“规范”其实是场景驱动的选择逻辑
| 场景 | 推荐方式 | 理由 |
|---|---|---|
| 仅需解构一层配置对象 |
{...obj} 或 Object.assign
|
快、简洁、无副作用 |
| 需要完整隔离状态(如 React state 更新、表单暂存) |
_.cloneDeep 或健壮递归实现 |
避免嵌套数据污染 |
| 临时序列化传输(如 localStorage、API body) |
JSON.stringify → JSON.parse
|
兼容性好,天然过滤不可序列化项 |
本质上,JavaScript 拷贝的“规范”不是语法层面的强制约定,而是基于内存模型理解 + 类型行为认知 + 实际需求权衡形成的工程共识。


















