浅谈深浅复制

浅谈深浅复制

在开发过程中,我们常常会遇到对复杂对象中数据的复制。这时候就存在两种情况,一个是复制引用,一个是复制实例。这也就是浅复制和深复制。

什么是深度复制和浅度复制呢?

深度复制和浅度复制都是针对像Array、Object等复杂对象而言的。
对对象而言,浅复制是指是对对象地址的复制,没有开辟新的栈,也就是复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会改变;而深复制则是开辟新的栈,两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。

浅复制

1
2
3
4
5
var testArray = [0,1,2]
var copyArray = testArray;
copyArray[0] = "change";
console.log(copyArray); //输出["change", 1, 2]
console.log(testArray); //输出["change", 1, 2]

由上面的例子可以看出,当改变复制数组中的值时,原来的数组值也会被改变。

深度复制

深度复制情况比较复杂

1.slice()和concat()方法

Array的slice和concat方法都会返回一个新的数组实例,但是这两个方法对于数组中的对象元素却没有执行深复制,而只是复制了引用了,因此这两个方法并不是真正的深复制,通过以下代码进行理解:

1
2
3
4
5
6
7
8
var array = [1,2,3];
var array_shallow = array;
var array_concat = array.concat();
var array_slice = array.slice(0);
array[0] = 2;
console.log(array_shallow);//[2,2,3]
console.log(array_concat);//[1,2,3]
console.log(array_slice)//[1,2,3]

这里的复制只复制了一层,如果有多层属性的话,还是指向同一个内存地址,看下方的例子:

1
2
3
4
5
6
7
8
9
10
11
var array = [1, [1,2,3], {name:"array"}];
var array_concat = array.concat();
var array_slice = array.slice(0);
//改变array_concat中数组元素的值
array_concat[1][0] = 5;
console.log(array[1]); //[5,2,3]
console.log(array_slice[1]); //[5,2,3]
//改变array_slice中对象元素的值
array_slice[2].name = "array_slice";
console.log(array[2].name); //array_slice
console.log(array_concat[2].name); //array_slice

这里的例子可以看出不是真正的深度复制,只是对对象进行了引用

2.Object.assign()

看下方的例子,相信你大概就明白了这个方法到底是深度复制还是浅复制

1
2
3
4
5
var obj = {a:1,b:2};
var objclone = Object.assign({},obj);
obj.c = 3;
console.log(obj); //输出 {a:1,b:2,c:3}
console.log(objclone); //输出{a:1,b:2}

再来一个~

1
2
3
4
5
6
7
8
9
10
11
12
let obj = {
a: 1,
b: 2,
c: {
age: 30
}
};
var objclone = Object.assign({},obj);
console.log('objclone: ', objclone);
obj.c.age = 45;
console.log('After Change - obj: ', obj.c.age); // 45 - This also changes
console.log('After Change - objclone: ', objclone.c.age); // 45

所以Object.assign() 只是一级属性复制,比浅拷贝多深拷贝了一层而已。对于多层属性的复制,它也是属于浅复制。

3.JSON实现

JSON对象下有两个方法,一是将JS对象转换成字符串对象的JSON.stringify方法;一个是将字符串对象转换成JS对象的JSON.parse方法。这两个方法结合使用可以实现对象的深复制。也就是说,当我们需要复制一个obj对象时,可以先调用JSON.stringify(obj),将其转换为字符串对象,然后再调用JSON.parse方法,将其转换为JS对象。就可以轻松的实现对象的深复制
看下方例子

1
2
3
4
5
6
7
8
9
10
11
var obj = {
a: 1,
b: 2,
c: {
age: 30
}
};
var copyObject = JSON.parse(JSON.stringify(obj));
copyObject.c.age = 45;
console.log(obj) //obj={a:1,b:2,c:{age:30}}
console.log(copyObject) //copyObject={a:1,b:2,c:{age:45}}

使用这种方式实现深复制有一个缺点就是必须给JSON.parse方法传入的字符串必须是合法的JSON,否则会抛出错误

4.采用递归解决

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
var testObject = {
a: 1,
b: 2,
c: {
age: 30
}
};
function deepCopy(obj,copy){
var copy = copy || {};
for(var i in obj){
if(typeof obj[i] === 'object'){
//要考虑深复制问题了
if(obj[i].constructor === Array){
copy[i] =[];
}else{
copy[i] = {};
}
deepCopy(obj[i],copy[i]);
}else{
copy[i] = obj[i];
}
}
return copy;
}
var copyObject = deepCopy(testObject);
copyObject.c.age = 45;
console.log(testObject) //{a: 1,b: 2,c: {age: 30}};

这种方法的缺点是对于数据庞大的对象运行效率较低

以上是对深度复制和浅度复制的理解,如果有不对的地方欢迎指正哟,相互学习讨论~