JavaScript拷贝大作战:浅拷贝vs深拷贝

说在前面

在现代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,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。

相关推荐
南宫生20 分钟前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_32 分钟前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
高山我梦口香糖36 分钟前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_7482352439 分钟前
前端实现获取后端返回的文件流并下载
前端·状态模式
落魄君子44 分钟前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
菜鸡中的奋斗鸡→挣扎鸡1 小时前
滑动窗口 + 算法复习
数据结构·算法
Lenyiin1 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
郭wes代码1 小时前
Cmd命令大全(万字详细版)
python·算法·小程序
scan7241 小时前
LILAC采样算法
人工智能·算法·机器学习
m0_748240252 小时前
前端如何检测用户登录状态是否过期
前端