前端面试中,面试官经常会提到关于浅拷贝和深拷贝的问题。但是我总是理解于它的表面,面试中再深挖一点就会卡壳,我想把我的理解写下来,希望可以帮助到大家,如果有错误的地方希望大家可以指正,以免误导~
看这篇文章之前,对于赋值的过程大家不了解的可以看我的这一篇文章https://blog.csdn.net/jisuaijicainiao/article/details/140844128?spm=1001.2014.3001.5501
浅拷贝的过程直接举例子说明能更加直观理解
首先说明一下浅拷贝的过程: 浅拷贝会创建一个新对象,并将原始对象的属性逐个复制到这个新对象中。然而,对于引用类型的属性(如对象、数组),浅拷贝只会复制引用,而不是复制引用类型的内容。
let obj1 = {
'name':'Lili',
'school':{
'address':"北京"
}
}
let obj2 = Object.assign({},obj1)
obj2.name = 'sasa'
obj2.school.address = '上海'
console.log(obj1.name) //'Lili'
console.log(obj1.school.address) //'上海'
上面代码的过程通过图片显示就如下,在未改变obj2任何值之前,内存分配中做了这些操作。浅拷贝就是重新创建了一个对象,把被拷贝对象的属性值都放进了自己的对象中。对于引用类型,值也只是存储了引用地址。
在改变obj2的属性值之后,如下图所示。因为obj2是在一个新的对象中,所以它的基本类型属性的修改不会影响到obj1。但是对于school属性,它是引用类型存储的是一个对象的地址,它跟obj1的school指向的是相同的一个对象。所以修改obj2就会影响到obj1的值。
但是还有一种情况就是以下代码
let obj1 = {
'name':'Lili',
'school':{
'address':"北京"
}
}
let obj2 = Object.assign({},obj1)
obj2.name = 'sasa'
obj2.school = {address:'上海'}
console.log(obj1.name) //'Lili'
console.log(obj1.school.address) //'北京'
图解的话就是下图这样的,obj2修改school属性的值,相当于重新创建了一个对象,在堆内存中重新开辟了一块空间,obj2对象的school属性指向新的对象地址。所以obj1和obj2此时不指向同一块空间,所以并不影响obj1对象。
浅拷贝的实现方式除了Object.assign()方法【let obj2 = Object.assign({}, obj1)】,还有扩展运算符‘...’【let obj2 = {...obj1}】,还有Array.prototype.slice()通常用于数组拷贝【let arr2 = arr1.slice()】,还有Array.prototype.concat()用于数组拷贝【let arr2 = arr1.concat()】等等。
深拷贝
深拷贝的过程:深拷贝会递归的遍历对象的每个属性,如果属性是基本类型就会直接复制值,如果属性是引用类型,深拷贝就会创建一个新的对象或数组,并递归的复制其中的所有属性。深拷贝后的对象与原对象在内存上是完全独立的。
举例说明
let obj1 = {
name:'Lili',
school:{
address:"北京"
}
}
let obj2 = JSON.parse(JSON.stringify(obj1))
obj2.name = 'sasa'
obj2.school.address = '上海'
console.log(obj1.name) //'Lili'
console.log(obj1.school.address) //'北京'
上面代码的图解过程,根据上述深拷贝的过程,在修改拷贝对象的值之前我们可以得到下图的结果。obj1和obj2是两个完全独立的对象。
当我们对obj2的属性值进行修改后,如下图所示。obj1是完全不受影响的,无论你怎么修改obj2的值都不会改变obj1的值。
实现深拷贝的方法除了JSON.pares(JSON.stringify())外【let obj2 = JSON.pares(JSON.stringify())】,还有递归遍历手动复制实现,还可用lodash库的_.cloneDeep()方法【let obj2 = _.cloneDeep(obj1)】
今天就到这里吧~ 继续加油