likes
comments
collection
share

深浅拷贝

作者站长头像
站长
· 阅读数 15

浅拷贝

  • 浅拷贝:对象拷贝给一个新的对象,开发中我们经常需要复制一个对象,如果直接赋值,则复制的是地址,修改任何一个对象,另一个对象都会变化。
//直接赋值复制的是地址 
const obj = { name: '佩奇' } 
const newObj = obj 
newObj.name = '乔治' 
console.log(obj.name) // 也变成了乔治

深浅拷贝

浅拷贝常见方法:

  1. 拷贝对象:
  • Object. assign()

  • 展开运算符:{...obj}

  1. 拷贝数组:
  • Array.prototype.concat()
  • 展开运算符 [...arr]

示例:

let p = {a: 1, b: {v: 2}};
let c = {...p};

p.a = 55;
p.b.v = 66;

console.log(p);  // {a: 55, b: {v: 66}}
console.log(c);  // {a: 1, b: {v: 66}}
  • 浅拷贝只会复制对象的第一层属性,而不会递归地复制嵌套对象。
  • 在上面的示例中,修改p.a的值只影响到p,因为a是一个顶层属性,而p.b.v的修改影响到了c.b.v,因为b是一个嵌套对象,c.bp.b引用同一个嵌套对象。

浅拷贝注意点:

  • 如果是基本数据类型拷贝值
  • 如果是引用数据类型拷贝的是地址,会相互影响。
  • 拷贝原对象第一层基本数据类型的值和引用类型的地址。
  • 第一层,如果属性值为引用类型 引用类型的值改变,会相互影响

深拷贝

  • 在JavaScript中,深拷贝(deep copy)是指创建一个新的对象或数组,并且递归地复制原始对象或数组的所有嵌套属性和元素。深拷贝是在新的内存空间中创建一个完全独立的副本,与原始对象或数组完全无关。
  • 在堆内存中新开辟一个空间,递归的拷贝原对象的所有属性和方法
  • 递归拷贝:一层一层,每一层对象都新创建一个内存空间
  • 深拷贝:新旧对象,不会共享内存空间,互不影响。

实现深拷贝的方式:

1. JSON.parse(JSON.stringify(obj))

  • 这个方法的原理是使用JSON.stringify()将原始对象或数组转换为JSON字符串,然后再使用JSON.parse()将JSON字符串解析回JavaScript对象或数组。通过这个过程,我们可以获得原始对象或数组的一个完全独立的副本。
  • JSON.stringify() 也叫做JSON序列化
  • JSON.parse() 反序列化
const originalObject = { a: 1, b: 2, c: { d: 3 } };
const deepCopyObject = JSON.parse(JSON.stringify(originalObject));

// 修改原始对象中的属性值
originalObject.a = 10;
originalObject.c.d = 30;

console.log(originalObject);        // { a: 10, b: 2, c: { d: 30 } }
console.log(deepCopyObject);        // { a: 1, b: 2, c: { d: 3 } }
// 修改原始对象不会影响到深拷贝的对象,它们是完全独立的。
  • 缺点:
  1. 无法复制函数、正则表达式等非原始数据类型,因为它们在序列化过程中会丢失。
  2. 对于循环引用的对象或数组,会导致无限递归的序列化,可能导致堆栈溢出的错误。
  3. 对象的原型链上的属性和方法也无法被复制
  4. 如果拷贝日期对象,会变成日期字符串
  5. 如果是NaN,Infinity, 属性值会变成null

2.lodash库

  • 使用lodash库提供的cloneDeep()函数可以进行深拷贝。cloneDeep()函数会递归地复制对象或数组及其嵌套的属性和元素,创建一个与原始对象完全独立的副本。
<!DOCTYPE html>
<html>
<head>
  <title>Lodash Deep Copy Example</title>
</head>
<body>
  <script src="path/to/lodash.js"></script>
  <script>
    const originalObject = { a: 1, b: 2, c: { d: 3 } };
    const deepCopyObject = _.cloneDeep(originalObject);

    // 修改原始对象中的属性值
    originalObject.a = 10;
    originalObject.c.d = 30;

    console.log(originalObject);        // { a: 10, b: 2, c: { d: 30 } }
    console.log(deepCopyObject);        // { a: 1, b: 2, c: { d: 3 } }
  </script>
</body>
</html>

  • 在上述示例中,我们在HTML文件中通过<script>标签引入了lodash库的源代码文件(lodash.js),然后使用_.cloneDeep()函数对原始对象进行深拷贝。因此,修改原始对象不会影响到深拷贝的对象,它们是完全独立的。
  • 确保在HTML文件中引入lodash库的位置和顺序正确,以确保在使用_.cloneDeep()函数之前已经成功加载了lodash库的源代码。

3.手写递归实现

function deepCopy(obj) {
  if (obj === null || typeof obj !== "object") {
    return obj;
  }

  let copy = Array.isArray(obj) ? [] : {};

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      copy[key] = deepCopy(obj[key]);
    }
  }

  return copy;
}

  • obj.hasOwnProperty(key)这一步用于过滤对象的原型链上的属性,确保只复制对象自身的属性。因为 for...in 循环会遍历对象的所有可枚举属性,包括从原型链继承的属性。
  1. 在上述代码中,deepCopy()函数接受一个参数obj,表示需要进行深拷贝的对象或数组。函数首先检查obj是否为null或不是一个对象,如果是,则直接返回obj。如果obj是一个对象或数组,我们创建一个新的空对象或数组copy,用于存储深拷贝后的副本。
  2. 然后,我们使用for...in循环遍历obj的属性或数组元素,并使用递归调用deepCopy()函数对每个属性或元素进行深拷贝。在每次递归调用中,如果属性或元素是一个对象或数组,我们将继续递归调用deepCopy()函数,直到遍历完所有的嵌套属性和元素
  3. 最后,我们返回深拷贝后的copy对象或数组,它是一个与原始对象或数组完全独立的副本。
  • 以下是一个使用deepCopy()函数进行深拷贝的示例:
const originalObject = { a: 1, b: 2, c: { d: 3 } };
const deepCopyObject = deepCopy(originalObject);

// 修改原始对象中的属性值
originalObject.a = 10;
originalObject.c.d = 30;

console.log(originalObject);        // { a: 10, b: 2, c: { d: 30 } }
console.log(deepCopyObject);        // { a: 1, b: 2, c: { d: 3 } }