赋值、浅拷贝、深拷贝
一、区别
在许多语言中都有浅拷贝与深拷贝,JavaScript也不例外,他们的区别很简单。
-
赋值:对象赋值时,实际上赋的是对象在栈中的地址,而不是在堆中的数据,两个对象指向同一个存储控件,无论哪个对象发生改变其改变的都是同一个存储空间的内容。
-
浅拷贝:创建一个新的对象,把源对象的值拷贝进来,这里会有两种情况:
- 对于基本类型变量,拷贝的是它的值。
- 对于引用类型变量,则拷贝的是内存地址。
-
深拷贝:将原始对象从内存中完整地拷贝一份出来到新的内存地址,因此修改原对象后,新对象不会改变。
借助ConardLi大佬的两张图:
简单来说就是浅拷贝对象共享内存,深拷贝则不共享内存。
虽然说浅拷贝和深拷贝的区别是很明显,但赋值与浅拷贝似乎还是有点迷惑,下面看看它们具体的区别,首先介绍一个浅拷贝的方法Object.assign()
,这个方法可以把任意多个的源对象自身的可枚举数学浅拷贝给目标对象,然后返回,如下:
let obj1 = {
name:'jjy',
attribute:{
age:18,
wight:138,
}
}
let obj3 = Object.assign({},obj1)
obj3.attribute.age = 23
obj3.name = 'wjh'
console.log(obj1) //{ name: 'jjy', attribute: { age: 23, wight: 138 } }
可以看到,在浅拷贝之后,对于基本类型的属性(name),我们即使修改了obj3的name,也不会导致源属性的修改,因为这里是直接开辟了一个新的内存空间来存放拷贝过来的值;而对于引用类型(attribute),我们修改了obj3的attribute之后,也会导致源对象的attribute里的值改变,可以看出这里只是拷贝了内存地址,实际上改变的是同一个内存空间存放的值。
而如果是赋值,如下:
let obj1 = {
name:'jjy',
attribute:{
age:18,
wight:138,
}
}
let obj2 = obj1
obj2.attribute.age = 23
obj2.name = 'wjh'
console.log(obj1) //{ name: 'wjh', attribute: { age: 23, wight: 138 } }
这样的话无论赋值过来的值也好,还是对象也好,指向的都是同一个内存空间(即只赋值了内存地址过来),一个改变,另一个也就改变了。
那么深拷贝的话就是无论是值还是对象,都是一个新的内存空间存放了,互不影响。
再次借用大佬的图总结一下三者的区别:
二、实现
2.1 浅拷贝实现
-
Object.assign()
上面说了。
-
展开运算符号(…)
这是ES6新增的一个特性,便于展开对象/数组中的元素,同时它也可以用于浅拷贝。
let obj1 = { name:'jjy', attribute:{ age:18, wight:138, } } let obj3 = {...obj1} obj3.attribute.age = 23 obj3.name = 'wjh' console.log(obj1) //{ name: 'jjy', attribute: { age: 23, wight: 138 } }
-
concat()
总所周知数组也是对象,concat()本质是用于连接数组的,但也能用于浅拷贝。
let obj1 = [ 1, 2, { age:18, wight:138, } ] let obj3 = obj1.concat() obj3[1] = 3 obj3[2].age = 23 console.log(obj1) //[ 1, 2, { age: 23, wight: 138 } ]
-
slice()
这个函数是用于提取数组某部分的,浅拷贝的用法和concat类似。
let obj1 = [ 1, 2, { age:18, wight:138, } ] let obj3 = obj1.slice() obj3[1] = 3 obj3[2].age = 23 console.log(obj1) //[ 1, 2, { age: 23, wight: 138 } ]
2.2 深拷贝实现
- JSON.parse(JSON.stringfy())
let obj1 = {
name:'jjy',
attribute:{
age:18,
wight:138,
}
}
let obj3 = JSON.parse(JSON.stringify(obj1))
obj3.attribute.age = 23
obj3.name = 'wjh'
console.log(obj1) //{ name: 'jjy', attribute: { age: 18, wight: 138 } }
奇妙的方法,通过将对象转成JSON字符串再转回来,产生了一个新的对象,实现深拷贝。
但这种方法不适用与函数,会将函数变为null。
-
手写深拷贝
这好像是面试题之一,这种方法的基本原理是遍历对象、数组,直到最里面的基本类型,将其一个个复制出来,就可以实现深拷贝了。
有几种方式,这里参考一下大佬的文章如何写出一个惊艳面试官的深拷贝,实在是太详细了~!以后有空我再自己一一实现。
三、总结
深浅拷贝看似简单,实际有很多知识点,尤其是深拷贝,要考虑的方面很多。