深浅拷贝区别
一、深浅拷贝的核心区别
深浅拷贝的本质差异在于:是否复制对象的 “嵌套引用类型属性”(如对象、数组、函数等),核心对比如下:
| 特性 |
浅拷贝(Shallow Copy) |
深拷贝(Deep Copy) |
| 拷贝层级 |
仅拷贝 “第一层” 属性,嵌套引用类型只复制引用(地址) |
递归拷贝所有层级,嵌套引用类型会创建新的独立对象 |
| 内存指向 |
嵌套对象 / 数组与原对象共享同一块内存 |
所有层级的对象 / 数组都有独立内存,互不影响 |
| 修改影响 |
修改拷贝后的嵌套属性,会同步影响原对象 |
修改拷贝后的任意属性,都不会影响原对象 |
| 实现复杂度 |
简单(无需递归) |
复杂(需递归处理所有嵌套层级,处理特殊类型) |
| 性能 |
效率高,占用内存少 |
效率低,占用内存多(递归创建新对象) |
二、直观示例:理解深浅拷贝的差异
基础场景:嵌套对象的拷贝
1 2 3 4 5 6 7
| const original = { name: "张三", age: 18, hobbies: ["读书", "运动"], address: { city: "北京" } };
|
1. 浅拷贝示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const shallowCopy = { ...original };
shallowCopy.name = "李四"; console.log(original.name);
shallowCopy.hobbies.push("游戏"); console.log(original.hobbies);
shallowCopy.address.city = "上海"; console.log(original.address.city);
|
2. 深拷贝示例
1 2 3 4 5 6 7 8 9 10
| const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.hobbies.push("游戏"); console.log(original.hobbies);
deepCopy.address.city = "上海"; console.log(original.address.city);
|
三、浅拷贝的常见实现方式
1. 对象浅拷贝
- **扩展运算符
{ ...obj }**(ES6+,最常用);
Object.assign({}, obj)(ES6+);
- 手动遍历第一层属性赋值。
1 2 3 4 5
| const obj = { a: 1, b: { c: 2 } };
const copy1 = { ...obj };
const copy2 = Object.assign({}, obj);
|
2. 数组浅拷贝
- **扩展运算符
[...arr]**;
arr.slice()(不传参数默认拷贝整个数组);
arr.concat()(空参数拼接,返回新数组)。
1 2 3 4 5 6 7
| const arr = [1, [2, 3]];
const copy1 = [...arr];
const copy2 = arr.slice();
const copy3 = arr.concat();
|
四、深拷贝的常见实现方式
1. 简易方案:JSON.parse(JSON.stringify(obj))(有局限性)
优点:简单易用,无需手写递归;
缺点
:无法处理以下类型:
- 函数、
undefined、Symbol(会被忽略);
- 循环引用(如
obj.a = obj,会报错);
- 特殊对象(
Date 转为字符串、RegExp 转为空对象、Map/Set 转为空对象)。
1 2 3 4 5 6 7 8 9 10 11 12
| const obj = { a: 1, b: () => {}, c: undefined, d: Symbol("s"), e: new Date(), f: /abc/, g: [1, 2] }; const copy = JSON.parse(JSON.stringify(obj)); console.log(copy);
|
2. 手动递归实现(自定义深拷贝)
处理基础类型、引用类型、循环引用等场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| function deepClone(target, map = new WeakMap()) { if (target === null || typeof target !== "object") { return target; }
if (map.has(target)) { return map.get(target); }
if (target instanceof Date) { return new Date(target); } if (target instanceof RegExp) { return new RegExp(target); }
const cloneTarget = Array.isArray(target) ? [] : {}; map.set(target, cloneTarget);
for (const key in target) { if (target.hasOwnProperty(key)) { cloneTarget[key] = deepClone(target[key], map); } }
if (target instanceof Map) { const cloneMap = new Map(); map.set(target, cloneMap); target.forEach((v, k) => cloneMap.set(k, deepClone(v, map))); return cloneMap; } if (target instanceof Set) { const cloneSet = new Set(); map.set(target, cloneSet); target.forEach(v => cloneSet.add(deepClone(v, map))); return cloneSet; }
return cloneTarget; }
const obj = { a: 1, b: [2, 3], c: new Date() }; obj.d = obj; const copy = deepClone(obj); console.log(copy.b === obj.b); console.log(copy.d === copy);
|
3. 第三方库(推荐,成熟稳定)
五、深浅拷贝的适用场景
浅拷贝适用场景
- 仅需拷贝 “无嵌套引用类型” 的简单对象 / 数组;
- 追求性能,无需独立嵌套属性(如临时复用第一层属性);
- 快速创建对象副本,且不修改嵌套属性。
深拷贝适用场景
- 拷贝包含嵌套对象 / 数组的复杂数据(如接口返回的复杂数据、表单状态);
- 需要修改拷贝后的数据,且不影响原数据;
- 处理包含循环引用、特殊对象(Date/RegExp/Map/Set)的场景。
六、核心总结
| 场景 |
选择拷贝方式 |
推荐实现 |
| 简单对象 / 数组(无嵌套) |
浅拷贝 |
扩展运算符 { ...obj }/[...arr] |
| 复杂对象(有嵌套 / 特殊类型) |
深拷贝 |
Lodash _.cloneDeep() |
| 临时使用、追求性能 |
浅拷贝 |
Object.assign / slice |
| 数据独立、避免副作用 |
深拷贝 |
自定义递归(简单场景)/_.cloneDeep(复杂场景) |