谈谈对浅拷贝和深拷贝的理解

192 阅读4分钟

1.前言

JavaScript中怎么去复制对象呢?我们到底复制的是值还是地址呢?如果你是模模糊糊的话,我这里有不同的两种理解方法,希望您能弄明白

  • 什么是深/浅拷贝
  • 深/浅拷贝的区别

2.数据类型储存

在理解深/浅拷贝之前先要搞清楚javascript中存在的两大数据类型

  • 基本类型
  • 引用类型 基本类型数据保存在栈内存中

引用类型数据保存在堆内存中,引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中

3.浅拷贝

3.1第一种理解

浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝

如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址,修改一个对象的地址会影响到另一个对象

3.1第二种理解

浅拷贝只能拷贝一层:如果对象中只有一层(对象的属性值不是对象与数组)它是可以正常工作了。
如果对象有多层(理解为多维数组)它就不能实现真正的 “拷贝”的功能了。

赋值实现

 //赋值实现
       let obj1 = { name: '张三' , age : 18 , hobby:[ 'sing' ,'dance' ]}
       let obj2 = obj1
       obj2.age = 30
       obj2.hobby.push('read')
       console.log('obj1',obj1);
       console.log('obj2',obj2);

Snipaste_2022-06-12_10-17-58.png

浅拷贝实现

 //Object.assign  
       let obj1 = { name: '张三' , age : 18 , hobby:[ 'sing' ,'dance' ]}
       let obj2 = Object.assign({} , obj1)
       obj2.age = 30
       obj2.hobby.push('read')
       console.log('obj1',obj1);
       console.log('obj2',obj2);
// 展开运算符 展开运算符是es6的特性,它提供了一种非常方便的方式来执行浅拷贝
       let obj1 = { name: '张三' , age : 18 , hobby:[ 'sing' ,'dance' ]}
       let obj2 = {...obj1}
       obj2.age = 30
       obj2.hobby.push('read')
       console.log('obj1',obj1);
       console.log('obj2',obj2);

Array.prototype.slice()。slice() 方法返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。

image.png

对比赋值和浅拷贝方法我们可以看见对象数据修改后对原始数据的影响

  • 赋值 基本数据类型改变会使原数据一同改变 包含子对象的也会改变
  • 浅拷贝 基本数据类型改变改变不会使原数据改变 包含子对象的会改变

4.深拷贝

深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

4.1实现方式

JSON.parse(JSON.stringify())

 //  JSON.parse(JSON.stringify())
       let obj1 = { name: '张三' , age : 18 , hobby:[ 'sing' ,'dance' ]}
       let obj2 = JSON.parse(JSON.stringify(obj1))
       obj2.age = 30
       obj2.hobby.push('read')
       console.log('obj1',obj1);
       console.log('obj2',obj2);

jQuery.extend()

 // jQuery.extend()
       let obj1 = { name: '张三' , age : 18 , hobby:[ 'sing' ,'dance' ]}
       let obj2 = $.extend(true,{},obj1)
       obj2.age = 30
       obj2.hobby.push('read')
       console.log('obj1',obj1);
       console.log('obj2',obj2);

image.png

对比于赋值和浅拷贝,不难发现无论是基本数据类型还是包含的子对象改变都不会影响原数据的改变

总结

这三者的区别如下,不过比较的前提都是针对引用类型

  • 当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。
  • 浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。
  • 深拷贝:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。
三种方式对原数据的影响
对原数据是否指向同一对象第一层数据为基本数据类型原数据中包含子对象
赋值改变会使原数据一同改变改变会使原数据一同改变
浅拷贝改变不会使原数据一同改变改变会使原数据一同改变
深拷贝改变不会使原数据一同改变改变不会使原数据一同改变

参考文章: 原文链接