面向对象的JavaScript,你需要知道的深拷贝
2023-09-08 01:39:02
为什么要学习深拷贝?
作为一名经验丰富的JavaScript开发人员,我经常在项目中遇到对象拷贝问题。JavaScript中存在两种拷贝方式:浅拷贝和深拷贝,区别在于对引用数据类型的处理。
基本数据类型(包括undefined、number、boolean、bigint和string)在内存中直接存储值,因此浅拷贝和深拷贝没有区别。
而引用数据类型(包括object、array和function)在内存中存储的是指向实际值的引用,浅拷贝只拷贝这个引用,而深拷贝则会拷贝引用所指向的实际值。这意味着深拷贝可以创建一个新对象,其中包含原始对象的完全副本,而浅拷贝创建的新对象仍然引用原始对象的同一个内存地址。
初学者往往会混淆浅拷贝和深拷贝,导致难以捉摸的程序错误。通过学习深拷贝,您可以避免这些陷阱,编写更健壮可靠的JavaScript代码。
基本数据类型和引用数据类型
在进一步探讨深拷贝之前,让我们先来回顾一下基本数据类型和引用数据类型的区别。
数据类型 | 存储方式 | 浅拷贝/深拷贝 |
---|---|---|
基本数据类型(undefined、number、boolean、bigint、string) | 直接存储值 | 无区别 |
引用数据类型(object、array、function) | 存储指向实际值的引用 | 浅拷贝只拷贝引用,深拷贝拷贝引用所指向的实际值 |
为了更好地理解,我们来看一个例子:
const num1 = 10;
const num2 = num1;
console.log(num1); // 输出:10
console.log(num2); // 输出:10
num1 = 20;
console.log(num1); // 输出:20
console.log(num2); // 输出:10
在这个例子中,我们定义了两个变量num1和num2,并让num2等于num1。此时,num1和num2都指向同一个内存地址,即存储数字10的地址。当我们把num1的值改为20时,num1指向了另一个内存地址,即存储数字20的地址,而num2仍然指向原来的内存地址,即存储数字10的地址。
这就是基本数据类型的浅拷贝行为。当我们拷贝基本数据类型的值时,实际上是复制了这个值本身,而不是复制指向该值的引用。
深拷贝
让我们回到深拷贝。深拷贝可以创建一个新对象,其中包含原始对象的完全副本。这意味着新对象和原始对象完全独立,彼此之间的任何更改都不会相互影响。
在JavaScript中,我们可以使用两种方法来实现深拷贝:
- JSON.parse(JSON.stringify()):这种方法将对象转换成JSON字符串,然后再将JSON字符串解析成一个新的对象。
- 递归:这种方法使用递归来遍历对象,并为每个属性创建一个新值。
以下是一个使用JSON.parse(JSON.stringify())方法实现深拷贝的例子:
const originalObject = {
name: 'John Doe',
age: 30,
address: {
street: '123 Main Street',
city: 'New York',
state: 'NY'
}
};
const copyObject = JSON.parse(JSON.stringify(originalObject));
console.log(originalObject); // 输出:{ name: 'John Doe', age: 30, address: { street: '123 Main Street', city: 'New York', state: 'NY' } }
console.log(copyObject); // 输出:{ name: 'John Doe', age: 30, address: { street: '123 Main Street', city: 'New York', state: 'NY' } }
originalObject.name = 'Jane Doe';
console.log(originalObject); // 输出:{ name: 'Jane Doe', age: 30, address: { street: '123 Main Street', city: 'New York', state: 'NY' } }
console.log(copyObject); // 输出:{ name: 'John Doe', age: 30, address: { street: '123 Main Street', city: 'New York', state: 'NY' } }
在这个例子中,我们使用JSON.parse(JSON.stringify())方法对originalObject进行深拷贝,创建了一个新的对象copyObject。然后,我们修改了originalObject的name属性,并将值改为'Jane Doe'。此时,我们发现copyObject的name属性仍然是'John Doe',这说明深拷贝成功地创建了一个与originalObject完全独立的新对象。
以下是一个使用递归方法实现深拷贝的例子:
function deepCopy(object) {
if (typeof object !== 'object' || object === null) {
return object;
}
const copy = Array.isArray(object) ? [] : {};
for (const key in object) {
copy[key] = deepCopy(object[key]);
}
return copy;
}
const originalObject = {
name: 'John Doe',
age: 30,
address: {
street: '123 Main Street',
city: 'New York',
state: 'NY'
}
};
const copyObject = deepCopy(originalObject);
console.log(originalObject); // 输出:{ name: 'John Doe', age: 30, address: { street: '123 Main Street', city: 'New York', state: 'NY' } }
console.log(copyObject); // 输出:{ name: 'John Doe', age: 30, address: { street: '123 Main Street', city: 'New York', state: 'NY' } }
originalObject.name = 'Jane Doe';
console.log(originalObject); // 输出:{ name: 'Jane Doe', age: 30, address: { street: '123 Main Street', city: 'New York', state: 'NY' } }
console.log(copyObject); // 输出:{ name: 'John Doe', age: 30, address: { street: '123 Main Street', city: 'New York', state: 'NY' } }
在这个例子中,我们使用递归方法对originalObject进行深拷贝,创建了一个新的对象copyObject。然后,我们修改了originalObject的name属性,并将值改为'Jane Doe'。此时,我们发现copyObject的name属性仍然是'John Doe',这说明深拷贝成功地创建了一个与originalObject完全独立的新对象。
结语
深拷贝是JavaScript开发中一项重要的技术,可以帮助我们避免浅拷贝带来的问题。通过学习深拷贝,我们可以编写更健壮可靠的JavaScript代码。