造成深拷贝和浅拷贝的主要原因还是因为数据存储的方式以及位置造成的。
在理解深拷贝和浅拷贝之前,咋们先了解下JavaScript有那些类型:
-
基本数据类型:Number、String、Boolean、undefined、Null、以及Es6新特性Symbol、或者未来Es10中的Bigint,一共是7种。
-
引用数据类型:Object,又细分为Object、Array、function、Data等等
粗糙来讲,一共是8种数据类型;
接下来我们讲讲基本和引用数据类型的存储原理:
- 基本数据类型:存储方法是直接将数据存储在栈中。
- 引用数据类型:存储方式是先在栈中开辟一个空间也就是咋们常说的入栈,存入一个地址,然后这个地址是指向堆中的某个位置,数据实际是存放在堆中。
有了上面的知识储备,咋们就好理解这个浅拷贝和深拷贝造成的原因了。比如咋们定义一个基本数据类型String,默认是直接在栈中存放数据。
var str = "这是一个字符串";
var newstr = str;
newstr = newstr + '1'
console.log(str, newstr);
咋们看效果,很明显修改newstr的值并不会影响str的值。这是由于在初始化newstr的时候,直接在栈中又给它开辟了一个新的空间。
咋们再来试试引用数据类型
var obj = {
name: "老王",
age: "30"
};
var newobj = obj;
newobj.name = "小王";
console.log(obj, newobj);
当我们将引用数据类型obj赋值给newobj后,修改newobj的属性,也会影响obj的属性。这就是浅拷贝。造成这种情况的原因是因为在赋值的过程中,仅仅是将栈中的地址进行了赋值,并没有在堆中给newobj重新开辟一个新的空间。导致obj和newobj指向同一个地址,这才是导致问题的所在。
讲完了造成原因,我们来讲讲如何去解决引用数据无法深拷贝这个问题。
在代码中运用递归的原因是,我们的对象是二维引用对象,但是只能对第一层进行深拷贝,很明显第二层并没有被深拷贝。
var obj = {
name: "老王",
age:"30",
child: {
name: "小王",
age: "3"
}
};
function copy_deep(obj){
var newobj = obj instanceof Object ? {}: [];
for(item in obj){
if(obj.hasOwnProperty(item)){
if(item instanceof Object){
newobj[item] = copy_deep(obj[item]);
}else{
newobj[item] = obj[item];
}
}
}
return newobj;
}
var new_obj = copy_deep(obj);
new_obj.name = "老李"
new_obj.child.name = "小李"
console.log(obj, new_obj);
对于这种多层的引用数据类型,咋们也可以用JSON的stringify和parse来操作。
function deepCopy(obj){
let _obj = JSON.stringify(obj);
let obj2 = JSON.parse(_obj);
return obj2;
}
var a = [1,[1,2],3,4];
var b = deepCopy(a);
b[1][0] = 2;
console.log(a,b);