说在前面
在现代JavaScript开发中,对象和数组的拷贝是一项常见但也容易被忽视的任务。然而,简单的赋值操作可能会导致意想不到的结果,因为它们只是创建了一个指向原始数据的引用。为了解决这个问题,JavaScript提供了深拷贝和浅拷贝两种拷贝方式。浅拷贝仅复制对象的引用,而深拷贝则创建一个全新的对象,完全独立于原始对象。本文将详细介绍这两种拷贝方式,包括它们的定义、应用场景以及如何在JavaScript中实现它们。无论您是初学者还是有一定经验的开发人员,本文都将帮助您更好地理解和应用深拷贝和浅拷贝,提升代码的可靠性和灵活性。让我们一起深入探索拷贝的世界吧!
深拷贝和浅拷贝的差异
深拷贝和浅拷贝都有各自适用的场景。深拷贝适用于需要完全独立的副本,并保持与原始对象的分离的情况。而浅拷贝适用于需要共享数据、降低内存占用和提高性能的情况。根据具体的需求和应用场景,选择适当的拷贝方式是至关重要的。
深拷贝
复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针和堆内存中的对象。(修改拷贝对象中的应用类型属性值时,原对象不会随着变化)
深拷贝是指创建一个完全独立于原始对象的副本,包括对象的所有嵌套属性和数组元素。深拷贝会递归地遍历原始对象,将其所有属性和元素复制到新的对象中,确保新对象与原始对象完全分离。这意味着对深拷贝创建的对象进行修改不会影响原始对象。
使用场景
- 当需要在两个独立的对象之间共享或传递数据时,使用深拷贝可以确保数据的独立性,避免意外的共享和修改。
- 当需要对对象进行修改或转换,但不希望影响原始对象时,可以使用深拷贝创建一个可安全操作的副本。
浅拷贝
复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针,不复制堆内存中的对象。(修改拷贝对象中的应用类型属性值时,原对象也会随着变化)
浅拷贝仅复制对象的引用,而不复制对象的实际值。换句话说,浅拷贝创建了一个新的对象,但该对象与原始对象共享相同的属性和元素。修改浅拷贝对象的属性或元素会影响原始对象。
使用场景
- 当需要在两个对象之间共享数据,且希望对其中一个对象进行修改时,可以使用浅拷贝。这样做可以减少内存占用和提高性能,因为不需要创建完全独立的副本。
- 当只需要访问对象的一部分属性或元素时,浅拷贝可以提供便利,避免复制整个对象的开销。
JS实现
浅拷贝
1、直接赋值
javascript
const originalObj = { name: 'John', age: 30 };
const newObj = originalObj;
newObj.name = 'jerry';
console.log(originalObj);
2、扩展运算符
使用扩展运算符可以快速简洁地进行对象和数组的浅拷贝。它会创建一个新的对象或数组,并将原始对象或数组的所有属性和元素复制到新的对象或数组中。
javascript
const originalObj = { name: 'John', age: 30, address:{city: 'Guangzhou'} };
const newObj = { ...originalObj };
newObj.address.city = 'Beijing';
console.log(originalObj);
javascript
const originalArr = [{ name: 'John'}];
const newArr = [...originalArr];
newArr[0].name = 'Jerry';
console.log(newArr);
3、Object.assign()
Object.assign() 方法可以将一个或多个源对象的属性复制到目标对象中,并返回目标对象。这个方法也可以用于浅拷贝对象。
javascript
const originalObj = { name: 'John', age: 30, address:{city: 'Guangzhou'} };
const newObj = Object.assign({}, originalObj);
newObj.address.city = 'Beijing';
console.log(newObj);
4、Array.slice()
对于数组,可以使用 Array.slice() 方法进行浅拷贝。该方法返回一个新的数组,包含原始数组中指定范围的元素。
javascript
const originalArr = [{ name: 'John'}];
const newArr = originalArr.slice();
newArr[0].name = 'Jerry';
console.log(newArr);
5、Array.concat()
Array.concat() 方法可以将两个或多个数组合并为一个新数组,并返回这个新数组。使用空参数调用 concat() 方法,可以实现对数组的浅拷贝。
javascript
const originalArr = [{ name: 'John'}];
const newArr = [].concat(originalArr);
newArr[0].name = 'Jerry';
console.log(newArr);
深拷贝
1、JSON.parse(JSON.stringify())
该方法是目前最常用的深拷贝方法之一。它基于 JSON 格式序列化和反序列化对象,将对象转换成字符串再转回对象,达到深拷贝的目的。
javascript
const originalObj = {
name: 'John',
age: 30,
address: {
city: 'Beijing',
country: 'China'
}
};
const newObj = JSON.parse(JSON.stringify(originalObj));
newObj.address.city = 'Guangzhou';
console.log('originalObj:',originalObj);
console.log('newObj:',newObj);
局限
- 无法拷贝函数、正则表达式等特殊对象。
- 无法处理循环引用的情况。
2、手动递归拷贝方法
这种方法基于手动递归复制对象及其属性,直到所有属性都是基本数据类型为止。
javascript
function deepClone(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const newObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
}
const originalObj = {
name: 'John',
age: 30,
address: {
city: 'Beijing',
country: 'China'
}
};
const newObj = deepClone(originalObj);
newObj.address.city = 'Guangzhou';
console.log('originalObj:',originalObj);
console.log('newObj:',newObj);
局限
- 无法拷贝循环引用的情况。
3、第三方库
除了以上两种方法,还有一些成熟的第三方库可以实现深拷贝,如 Lodash、jQuery 等。这些库提供了更为全面和高效的深拷贝功能,可以满足各种复杂对象的拷贝需求。
javascript
const originalObj = {
name: 'John',
age: 30,
address: {
city: 'Beijing',
country: 'China'
}
};
const newObj = _.cloneDeep(originalObj);
newObj.address.city = 'Guangzhou';
console.log(newObj);
局限
- 使用第三方库会增加项目的依赖和代码体积
公众号
关注公众号『前端也能这么有趣
』,获取更多新鲜内容。
说在后面
🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『
前端也能这么有趣
』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。