Object.assign
和
{...obj}
都属于浅拷贝,下面咱们讲解如何使用JS实现深拷贝。
这是JS实现深拷贝最简单的方法了,原理就是先将对象转换为字符串,再经过JSON.parse从新创建一个对象。 可是这种方法的局限也不少:javascript
咱们依次看看这三点,咱们测试一下这段代码:java
let obj = {
reg : /^asd$/,
fun: function(){},
syb:Symbol('foo'),
asd:'asd'
};
let cp = JSON.parse(JSON.stringify(obj));
console.log(cp);复制代码
结果:数组
能够看到,函数、正则、Symbol都没有被正确的复制。函数
若是在JSON.stringify
中传入一个循环引用的对象,那么会直接报错:测试
在说第三点以前,咱们看看这段代码:ui
let obj = { asd:'asd' };
let obj2 = {name:'aaaaa'};
obj.ttt1 = obj2;
obj.ttt2 = obj2;
let cp = JSON.parse(JSON.stringify(obj));
obj.ttt1.name = 'change';
cp.ttt1.name = 'change';
console.log(obj,cp);复制代码
在原对象 obj 中的 ttt1 和 ttt2 指向了同一个对象 obj2,那么我在深拷贝的时候,就应该只拷贝一次 obj2 ,下面咱们看看运行结果:spa
咱们能够看到(上面的为原对象,下面的为复制对象),原对象改变 ttt1.name 也会改变 ttt2.name ,由于他们指向相同的对象。code
可是,复制的对象中,ttt1 和 ttt2 分别指向了两个对象。复制对象没有保持和原对象同样的结构。所以,JSON实现深复制不能处理指向相同引用的状况,相同的引用会被重复复制。regexp
JS原生的方法不能很好的实现深复制,那么咱们就动手实现一个。cdn
思想很是简单:对于简单类型,直接复制。对于引用类型,递归复制它的每个属性。
咱们须要解决的问题:
实现代码:
function deepCopy(target){
let copyed_objs = [];//此数组解决了循环引用和相同引用的问题,它存放已经递归到的目标对象
function _deepCopy(target){
if((typeof target !== 'object')||!target){return target;}
for(let i= 0 ;i<copyed_objs.length;i++){
if(copyed_objs[i].target === target){
return copyed_objs[i].copyTarget;
}
}
let obj = {};
if(Array.isArray(target)){
obj = [];//处理target是数组的状况
}
copyed_objs.push({target:target,copyTarget:obj})
Object.keys(target).forEach(key=>{
if(obj[key]){ return;}
obj[key] = _deepCopy(target[key]);
});
return obj;
}
return _deepCopy(target);
}
复制代码
copyed_objs 这个数组存放的是已经递归过的目标对象。在递归一个目标对象以前,咱们应该检查这个数组,若是当前目标对象和 copyed_objs 中的某个对象相等,那么不对其递归。
这样就解决了循环引用和相同引用的问题。
测试一下代码:
var a = {
arr:[1,2,3,{key:'123'}],//数组测试
};
a.self = a;//循环引用测试
a.common1 = {name:'ccc'};
a.common2 = a.common1;//相同引用测试
var c = deepCopy(a);
c.common1.name = 'changed';
console.log(c);复制代码
结果:
能够看到,前文提到的问题都已经解决。
本文实现的深拷贝仅仅是解决了深复制的关键问题,还须要针对不一样的数据类型进行完善。