JS的深复制和浅复制

这里我们讨论一下JS的复杂引用类型的复制,不讨论简单数据类型的复制。 因为简单数据类型的值存在栈中,不存在引用值的情况。
对象的实例是存储在堆内存中,我们通过一个引用值去操作对象,所以在复制对象的时候就存在两种情况了:复制引用和复制实例。这就是深复制和浅复制的区别。

浅复制:复制引用,复制后的引用都指向同一个实例,如果其中一个引用的值改变了,另一个引用的值也会改变
深复制:深复制 不是简单的复制引用,而是在堆中重新分配内存,并把源对象实例的所有属性进行新建复制,复制之后的对象与源对象完全分离

对于深复制,如果源对象中有对象属性,就需要用到递归复制,保证复制对象和源对象完全分离。

浅复制的实现

1
2
3
4
5
6
7
function(obj) {
var copyObj = {};
for (var key in obj) {
copyObj[key] = obj[key]
}
return copyObj;
}

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

他们虽返回了不同的数组实例,但是却不是真正的深复制,依然只是复制了引用,引用的对象改变了,源对象依然改变

json对象的parse和stringify

1
2
3
4
5
6
7
8
9
var source = { name:"source", child:{ name:"child" } }
var target = JSON.parse(JSON.stringify(source)); //改变target的name属性
target.name = "target";
console.log(source.name); //source
console.log(target.name); //target
//改变target的child
target.child.name = "target child";
console.log(source.child.name); //child
console.log(target.child.name); //target child

从代码可以看出,复制之后的对象与源对象完全分离,这个方法可以处理所有能用json格式表示的数据类型,但是对于正则表达式类型、函数类型等无法深度复制
,而且会直接丢失相应的值。如果对象中存在循环引用,也无法正确处理。