JavaScript 中深拷贝浅拷贝的区别?如何实现一个深拷贝?
一、区别:像“复印文件” vs “创建副本文件”
1. 浅拷贝(Shallow Copy)
- 动作:只复制文件的第一页,其他页用“贴标签”指向原文件。
 - 结果:
修改第一层属性不影响原对象,但修改嵌套对象会同时改变原对象和拷贝对象。 - 例子:
const original = { a: 1, b: { c: 2 } }; const shallowCopy = { ...original }; // 展开运算符(浅拷贝)shallowCopy.a = 100; // 原对象不变:original.a = 1 shallowCopy.b.c = 200; // 原对象也被修改:original.b.c = 200 
2. 深拷贝(Deep Copy)
- 动作:把文件每一页都完整复印一份,完全独立。
 - 结果:
修改拷贝对象的任何属性都不会影响原对象。 - 例子:
const original = { a: 1, b: { c: 2 } }; const deepCopy = JSON.parse(JSON.stringify(original)); // 深拷贝deepCopy.a = 100; // 原对象不变:original.a = 1 deepCopy.b.c = 200; // 原对象不变:original.b.c = 2 
二、如何实现深拷贝?
方法1:JSON 序列化(简单但局限)
- 代码:
function simpleDeepCopy(obj) {return JSON.parse(JSON.stringify(obj)); } - 缺点: 
- 无法拷贝函数、
undefined、Symbol。 - 会丢失 Date 对象(转成字符串)、正则表达式(转成空对象)。
 - 无法处理循环引用(如 
obj.self = obj)。 
 - 无法拷贝函数、
 
方法2:递归克隆(手动实现完整版)
代码步骤分解:
function deepClone(obj, cache = new WeakMap()) {// 1. 处理基本类型和 null/undefinedif (obj === null || typeof obj !== 'object') return obj;// 2. 处理循环引用(避免无限递归)if (cache.has(obj)) return cache.get(obj);// 3. 处理特殊对象类型let clone;if (obj instanceof Date) {clone = new Date(obj); // 克隆日期} else if (obj instanceof RegExp) {clone = new RegExp(obj); // 克隆正则} else if (obj instanceof Map) {clone = new Map(Array.from(obj, ([key, val]) => [key, deepClone(val, cache)])); // 克隆Map} else if (obj instanceof Set) {clone = new Set(Array.from(obj, val => deepClone(val, cache))); // 克隆Set} else if (Array.isArray(obj)) {clone = []; // 克隆数组} else {clone = Object.create(Object.getPrototypeOf(obj)); // 克隆普通对象(保留原型链)}// 4. 缓存当前对象,防止循环引用cache.set(obj, clone);// 5. 递归拷贝所有属性for (const key in obj) {if (obj.hasOwnProperty(key)) {clone[key] = deepClone(obj[key], cache);}}return clone;
}
 
方法3:使用现成库(推荐实际开发使用)
- Lodash:
import _ from 'lodash'; const deepCopy = _.cloneDeep(original); - 优点:处理了所有边界情况(函数、循环引用、特殊对象等)。
 
三、不同场景选择建议
| 场景 | 推荐方法 | 
|---|---|
| 简单对象(无函数、日期) | JSON.parse(JSON.stringify()) | 
| 复杂对象(含特殊类型) | Lodash 的 cloneDeep | 
| 需要手动控制 | 递归克隆实现 | 
总结
- 浅拷贝:只复制表层,嵌套对象共享。
 - 深拷贝:完全独立副本,彻底隔离。
 - 实际开发:优先使用 Lodash 的 
cloneDeep,避免重复造轮子! 
