javascript 深拷贝和浅拷贝的区别及具体实现方案
一、核心区别
特性 | 浅拷贝 | 深拷贝 |
---|---|---|
复制层级 | 仅复制对象的第一层属性 | 递归复制对象的所有层级属性(包括嵌套对象和数组) |
引用关系 | 嵌套对象/数组与原对象共享内存(引用拷贝) | 嵌套对象/数组与原对象完全独立(值拷贝) |
修改影响 | 修改拷贝后的嵌套属性会影响原对象 | 修改拷贝后的任何属性都不会影响原对象 |
适用场景 | 简单数据结构,无需嵌套独立性 | 复杂数据结构,要求完全独立 |
二、浅拷贝实现方案
1. 对象浅拷贝
Object.assign()
const obj = { a: 1, b: { c: 2 } }; const shallowCopy = Object.assign({}, obj);
- 展开运算符(
...
)const obj = { a: 1, b: { c: 2 } }; const shallowCopy = { ...obj };
2. 数组浅拷贝
Array.prototype.slice()
const arr = [1, 2, [3, 4]]; const shallowCopy = arr.slice();
- 展开运算符(
...
)const arr = [1, 2, [3, 4]]; const shallowCopy = [...arr];
三、深拷贝实现方案
1. JSON 序列化法
原理:通过 JSON.stringify()
和 JSON.parse()
序列化对象。
优点:简单快捷,适合纯数据对象。
缺点:无法处理函数、undefined
、Symbol
、Date
、RegExp
、循环引用等。
const obj = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(obj));
2. 递归深拷贝(手动实现)
原理:递归遍历对象属性,处理所有数据类型。
优点:可自定义逻辑,支持复杂场景。
缺点:需处理边界条件(如循环引用)。
function deepClone(source, hash = new WeakMap()) {// 处理基本类型和 null/undefinedif (source === null || typeof source !== 'object') return source;// 处理循环引用if (hash.has(source)) return hash.get(source);// 处理 Date 和 RegExpif (source instanceof Date) return new Date(source);if (source instanceof RegExp) return new RegExp(source);// 初始化拷贝对象(保持原型链)const target = new source.constructor();hash.set(source, target);// 遍历所有属性(包括 Symbol)Reflect.ownKeys(source).forEach(key => {target[key] = deepClone(source[key], hash);});return target;
}
3. 第三方库实现
-
Lodash(推荐)
import { cloneDeep } from 'lodash'; const deepCopy = cloneDeep(obj);
-
jQuery
const deepCopy = $.extend(true, {}, obj);
四、特殊场景处理
1. 循环引用
问题:对象属性间接引用自身,导致递归栈溢出。
解决方案:使用 WeakMap
缓存已拷贝对象。
const obj = { a: 1 };
obj.self = obj; // 循环引用
const copy = deepClone(obj); // 递归实现中通过 WeakMap 避免死循环
2. 处理特殊对象
Date
对象:通过new Date(source.getTime())
重建。RegExp
对象:通过new RegExp(source.source, source.flags)
重建。Map
/Set
:遍历并递归拷贝每个元素。
3. 函数拷贝
问题:函数无法被完全拷贝(可能依赖闭包环境)。
解决方案:通过 eval
或 new Function
重建函数(需谨慎使用)。
五、性能对比与选型建议
方法 | 性能 | 功能完整性 | 安全性 |
---|---|---|---|
JSON 法 | 高 | 低(丢失类型) | 安全 |
递归实现 | 中 | 高 | 需处理边界 |
Lodash | 中 | 高 | 安全 |
选型建议:
- 纯数据对象且无特殊类型 → JSON 法。
- 复杂对象或需要保留类型 → Lodash 的 cloneDeep。
- 定制化需求 → 手动递归实现。
六、验证示例
const origin = { a: 1, b: { c: 2 }, d: new Date(), e: /regex/, f: function() { console.log('test') },g: [1, 2, { h: 3 }]
};
origin.self = origin; // 循环引用const copy = deepClone(origin);// 验证
console.log(copy.b === origin.b); // false(深拷贝成功)
console.log(copy.d instanceof Date); // true(保留类型)
console.log(copy.self === copy); // true(循环引用处理成功)