深浅拷贝

拷贝也是JS的重难点之一

数组的浅拷贝

我们知道数组的一些方法,比如slice,concat返回一个新数组的特性来实现拷贝

1
2
3
4
5
var oldArr = ['old','a',true];
var newArr = oldArr.concat();
newArr[0]='new';
console.log(oldArr);//['old','a',true]
console.log(newArr);//['new','a',true]

但是如果数组中嵌套了数组或者对象,看看结果如何:

1
2
3
4
5
6
var oldArr = [{name:'xiaojie'},['old']];
var newArr = oldArr.concat();
newArr[0].name = 'xiaobao';
newArr[1][0] = 'new';
console.log(oldArr);//[{name:'xiaobao'},['new']]
console.log(newArr);//[{name:'xiaobao'},['new']]

可以发现,改变了newArr的值,oldArr的值也跟着改变。
当数组元素是基本类型的时候,新旧数组的值互不影响,当数组元素为Object类型的时候,改变新数组的值会影响就旧数组的值。
我们把复制引用对象的拷贝方法称为浅拷贝,与之对应的就是深拷贝,深拷贝就是完全的拷贝一个对象,即使嵌套对象两者也互相分离,修改一个对象的属性,也不会影响另一个

数组的深拷贝

介绍一个简单粗暴的深拷贝方法,不仅适用于数组,还适用于对象:JSON.parse(JSON.stringify(oldArr))。

1
2
3
4
5
6
7
var oldArr = [{name:'xiaojie'},['old'],'old',true];
var newArr = JSON.parse(JSON.stringify(oldArr));
newArr[0].name='bao';
newArr[1][0]='new';
newArr[2]='new';
console.log(oldArr);// [{name:'xiaojie'},['old'],'old',true]
console.log(newArr);// [{name:'bao'},['new'],'new',true]

PS:方法JSON.parse(JSON.tostringify(arr))不能拷贝函数

深拷贝的实现

实现原理:试想一下,我们先拷贝最外层的基础数据类型,遇到引用类型时,在循环拷贝引用类型里面的基础类型,就可以实现深拷贝,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function deepAssign(obj){
if(typeof obj !=="object"){
return;
}
var newObj=obj instanceof Array?[]:{};
Object.keys(obj).forEach(function(item,index){
newObj[item]=typeof obj[item] === 'object'?deepAssign(obj[item]):obj[item];

})
return newObj;
}
var oldObj={
name:'jiejie',
age:24,
friends:{
name:'baobao',
age:23
}
}
var objOld={
name:'li',
friends:{
name:'xiaodebao'
}
}
var objNew=deepAssign(objOld);
objNew.friends.name='nihaoma';
objNew.name='nihaoma';
console.log(objOld.name);//li
console.log(objNew.name);//nihaoma;
console.log(objOld.friends.name);//xioadebao
console.log(objNew.friends.name);//nihaoma

ES6新增的Object.assign()

用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。来看看Object.assign()是深拷贝还是浅拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var oldObj={
name:'jiejie',
age:24,
friends:{
name:'baobao',
age:23
}
}
var newObj = Object.assign({},oldObj);
oldObj.name ="xiaoxiao";
oldObj.friends.name="dingding";
console.log(oldObj.name);//xiaoxiao
console.log(newObj.name);//jiejie;
console.log(newObj.friends.name);//dingding
console.log(newObj .friends.name);//dingding

由此可见Object.assign()也是浅拷贝。如果对象属性是基本类型,则互不影响,如果对象属性也是对象,则是复制的对象的应用