深拷贝,浅拷贝的概念
简单点来说,就是假设通过B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,如果B没变,那就是深拷贝。
为什么拷贝有深浅之分
我们来举个浅拷贝例子:
1 | let a=[0,1,2,3,4], |
结果如下:
b复制给了a,为啥修改数组a,数组b也跟着变了。
那么这里,就得引入基本数据类型与引用数据类型的概念了
基本数据类型有,number,string,boolean,null,undefined,symbol以及未来ES10新增的BigInt(任意精度整数)七类。
引用数据类型(Object类)有常规名值对的无序对象{a:1},数组[1,2,3],以及函数等。
而这两类数据存储分别是这样的:
a.基本类型–名值存储在栈内存中,例如let a=1;
当你b=a复制时,栈内存会新开辟一个内存,例如这样:
所以当你此时修改a=2,对b并不会造成影响,因为此时的b已自食其力,翅膀硬了,不受a的影响了。当然,let a=1,b=a;虽然b不受a影响,但这也算不上深拷贝,因为深拷贝本身只针对较为复杂的object类型数据。
b.引用数据类型–名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值,我们以上面浅拷贝的例子画个图:
当b=a进行拷贝时,其实复制的是a的引用地址,而并非堆里面的值。
而当我们a[0]=1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝了。
那,要是在堆内存中也开辟一个新的内存专门为b存放值,就像基本类型那样,岂不就达到深拷贝的效果了
实现深拷贝的方法
利用 递归 来实现深复制,对属性中所有引用类型的值,遍历到是基本类型的值为止
1 | function deepClone(source){ |
检测一下
1 | var a = {name:"jack",age:20}; |
jQuery中的 extend(true, target, object1 [, objectN ])
$.extend( [deep ], target, object1 [, objectN ] )
deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
object1 objectN可选。 Object类型 第一个以及第N个被合并的对象。
深拷贝:
1 | var obj = {name:'xixi',age:20,company : { name : '腾讯', address : '深圳'} }; |
浅拷贝
1 | var obj = {name:"xixi",age:20}; |
通过引入js的实用库 Lodash
例子:
1 | var objects = [{ 'a': 1 }, { 'b': 2 }]; |
实现浅拷贝的方法
JSON.parse(JSON.stringify())
1 | var syb = Symbol('obj'); |
ps: 当值为undefined、function、symbol 会在转换过程中被忽略。。。所以,对象值有这三种的话用这种方法会导致属性丢失。
Array 的 slice 和 concat 方法
1 | var a = [[1,2,3],4,5]; |
Object.assgin()
1 | var person1 = { |
其实总结一下就是:
Array 的 slice 和 concat 和 Object.assign(),他们都会复制第一层的值,对于 第一层 的值都是 深拷贝,而到 第二层 如果key是引用类型的时候 就是 浅拷贝 。
总结
如果要复制的对象或者数组都是简单数据类型,放心大胆的使用。如果存在深层次的引用,选择方法的时候要慎重。