深浅拷贝:
内存中一共分为栈内存和堆内存两大区域,所谓深浅拷贝主要是对js引用类型数据进行拷贝一份,浅拷贝就是引用类型数据相互赋值之后,例obj1=obj2;如果后面的操作中修改obj1或者obj2,这个时候数据是会进行相应的变化的,因为在内存中引用类型数据是存储在堆内存中,堆内存中存放的是引用类型的值,同时会有一个指针地址指向栈内存,两个引用类型数据地址一样,如果其中一个发生变化另外一个都会有影响;而深拷贝则不会,深拷贝是会在堆内存中重新开辟一块空间进行存放;
简单来说就是B复制了A,如果A发生了改变,如果B随之变化,那么是浅拷贝,如果B并没有发生变化,则是深拷贝。
基本类型拷贝
let a = 1;
let b = a;
b = 2;
console.log(a);//1
console.log(b);//2
a,b都是属于基本类型,基本类型的复制是不会影响对方的,因为基本类型是每一次创建变量都会在栈内存中开辟一块内存,用来存放值,所以对基本类型进行拷贝是不会对另外一个变量有影响的,属于深拷贝。
数组拷贝
concat() slice()
// concat()
let list = ['a','b','c'];
let list2 = list.concat();
list2.push('d')
console.log(list);//['a','b','c']
console.log(list2);//['a','b','c','d']
// slice()
let list = ['a','b','c'];
let list2 = list.slice();
list2.push('d')
console.log(list);//['a','b','c']
console.log(list2);//['a','b','c','d']
上面两种方法只能实现数组类型里面的单层深拷贝,如果是多层无法实现深拷贝。
//二维数组
let list = ['a','b','c',['d','e','f']];
let list2 = list.concat();
list2[3][0] = 'a';
console.log(list);
console.log(list2);
可以看到原二维数组的值也发生了变化,说明没有实现深拷贝。
由此总结,concat和slice只能实现一维数组的深拷贝,不能对多维数组进行深拷贝,所以并不是一个完美的深拷贝方式。
对象拷贝
new Object()
let a = {id:1,name:'a',obj:{id:999}};
let b = new Object();
b.id = a.id;
b.name = a.name;
b.obj = a.obj;
a.name = 'b';
a.obj.id = 888;
console.log(a);
console.log(b);
a.name更改并没有影响到b.name,好像可以看作深拷贝,但第二层obj里面里面的id随之更改了,因此其实并不是深拷贝。
Object.assign
let a = {id:1,name:'a',obj:{id:999}};
function fun(obj){
let o = {};
Object.assign(o,obj);
return o;
}
let a2 = fun(a);
a2.name ='a2';
a2.obj.id = 888;
console.log(a);
console.log(a2);
以上两种方法,对于一层对象都能实现深拷贝,但对于多层对象则无法实现,因此也不是一种完美的深拷贝方式。
JSON.parse(JSON.stringify( ))
let a = {
name : 'a',
age : 20,
obj : {id:999},
action : function(){
console.log(this.name);
}
}
let b = JSON.parse(JSON.stringify(a));
a.name = 'b';
a.obj.id = 888;
console.log(a);
console.log(b);
单层对象name和多层对象obj.id的改变都没影响到原对象,因此是一个比较完美的深拷贝,但是能看到function好像并没有被拷贝。
可以看出这个方法对于一层和多层都能实现深拷贝,但是这个方法的缺陷是不能拷贝Function,所以在使用时,一定要注意数据类型。
递归
let a = {
name:'a',
skin:["red","blue","yellow",["123","456"]],
child:{
work:'none',
obj:{
id:999
}
},
action:function(){
console.log(this.name);
}
}
//封装的递归方法
function copyWid(obj){
let newObj = Array.isArray(obj)?[]:{};
for (var i in obj){
if(typeof obj[i] === 'object'){ //判断是不是对象(数组或对象)
newObj[i] = copyWid(obj[i]) //递归解决多层拷贝
}else{
newObj[i] = obj[i]
}
}
return newObj;
};
let b = copyWid(a);
b.child.obj.id =888;
b.skin[3][0] = "pink";
console.log(a);
console.log(b);
可以看到,无论是多层的对象还是多层的数组,都能实现深拷贝,而且能拷贝函数,这个是目前来说最完美的深拷贝方法。
通过这个递归能实现一个比较完美的深拷贝,能弥补上述提到的所有方法中的缺点。
展开运算符
let a = {name:'a',id:99};//如果是数组[xx,xx,xx],{...a}需要改成[...a]
let b ={...a}; //[...a]
a.id =88;
console.log(a);
console.log(b);
这个方法能实现对象数组单层时的深拷贝,但是多层无法实现深拷贝。
以上这么多方法,最优解应该属于JSON和递归的方法(递归解决了JSON无法拷贝函数方法的问题),但是根据需求数据类型的不同,可以选择更简单的方式。
版权归原作者 Beannnnnnn 所有, 如有侵权,请联系我们删除。