在编程中,我们经常需要复制数据。然而,并不是所有的复制操作都是一样的。根据复制的深度,我们可以将复制分为浅拷贝(Shallow Copy)和深拷贝(Deep Copy)。这两种方式有着本质的区别,本文将深入探讨浅拷贝和深拷贝的概念、实现方式以及使用场景。
1. 什么是浅拷贝?
1.1 定义
浅拷贝是指创建一个新对象,并将原对象的属性值复制到新对象中。如果属性是基本类型(如number
、string
、boolean
),则直接复制值;如果属性是引用类型(如object
、array
),则复制引用地址。
换句话说,浅拷贝只复制对象的第一层属性,而不会递归复制嵌套的对象。
1.2 实现方式
在JavaScript中,浅拷贝可以通过多种方式实现:
1.2.1 使用Object.assign
1 2 3 4
| const obj = { a: 1, b: { c: 2 } }; const shallowCopy = Object.assign({}, obj);
console.log(shallowCopy);
|
1.2.2 使用展开运算符(...
)
1 2 3 4
| const obj = { a: 1, b: { c: 2 } }; const shallowCopy = { ...obj };
console.log(shallowCopy);
|
1.2.3 数组的浅拷贝
1 2 3 4
| const arr = [1, 2, { a: 3 }]; const shallowCopy = arr.slice();
console.log(shallowCopy);
|
1.3 浅拷贝的特点
- 复制引用:如果属性是引用类型,浅拷贝只会复制引用地址,新旧对象共享同一块内存。
- 修改影响:修改浅拷贝对象中的引用类型属性,会影响原对象。
1 2 3 4 5 6
| const obj = { a: 1, b: { c: 2 } }; const shallowCopy = { ...obj };
shallowCopy.b.c = 3;
console.log(obj.b.c);
|
2. 什么是深拷贝?
2.1 定义
深拷贝是指创建一个新对象,并递归地复制原对象的所有属性,包括嵌套的对象和数组。深拷贝会完全复制对象的结构和数据,新旧对象不共享同一块内存。
2.2 实现方式
在JavaScript中,深拷贝可以通过以下方式实现:
2.2.1 使用JSON.parse
和JSON.stringify
1 2 3 4
| const obj = { a: 1, b: { c: 2 } }; const deepCopy = JSON.parse(JSON.stringify(obj));
console.log(deepCopy);
|
注意:这种方式无法复制函数、undefined
、Symbol
等特殊类型。
2.2.2 使用递归函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function deepClone(obj) { if (obj === null || typeof obj !== 'object') { return obj; }
const clone = Array.isArray(obj) ? [] : {};
for (let key in obj) { if (obj.hasOwnProperty(key)) { clone[key] = deepClone(obj[key]); } }
return clone; }
const obj = { a: 1, b: { c: 2 } }; const deepCopy = deepClone(obj);
console.log(deepCopy);
|
2.2.3 使用第三方库(如Lodash)
1 2 3 4 5 6
| const _ = require('lodash');
const obj = { a: 1, b: { c: 2 } }; const deepCopy = _.cloneDeep(obj);
console.log(deepCopy);
|
2.3 深拷贝的特点
- 完全复制:深拷贝会递归复制所有嵌套的对象和数组,新旧对象互不影响。
- 修改不影响:修改深拷贝对象中的属性,不会影响原对象。
1 2 3 4 5 6
| const obj = { a: 1, b: { c: 2 } }; const deepCopy = JSON.parse(JSON.stringify(obj));
deepCopy.b.c = 3;
console.log(obj.b.c);
|
3. 浅拷贝 vs 深拷贝
特性 |
浅拷贝 |
深拷贝 |
复制深度 |
只复制第一层属性 |
递归复制所有嵌套属性 |
引用类型 |
复制引用地址,新旧对象共享内存 |
完全复制数据,新旧对象互不影响 |
性能 |
较高(只复制一层) |
较低(递归复制所有层级) |
适用场景 |
简单对象,无需复制嵌套结构 |
复杂对象,需要完全独立的副本 |
实现方式 |
Object.assign 、展开运算符、slice 等 |
JSON.parse(JSON.stringify) 、递归函数等 |
4. 使用场景
4.1 浅拷贝的使用场景
- 简单对象的复制:当对象结构简单,且没有嵌套引用类型时,浅拷贝是高效的选择。
- 性能优先的场景:如果不需要复制嵌套结构,浅拷贝可以节省内存和计算资源。
4.2 深拷贝的使用场景
- 复杂对象的复制:当对象结构复杂,且包含嵌套引用类型时,深拷贝是必要的。
- 数据隔离的场景:如果希望新旧对象完全独立,互不影响,必须使用深拷贝。
5. 使用建议
在实际开发中,我们需要根据具体需求选择合适的复制方式。如果你不确定该用哪种方式,记住一个简单的原则:如果对象包含嵌套引用类型,且需要完全独立的副本,就使用深拷贝;否则,浅拷贝通常就足够了。