前言
今天在使用Object.assign()方法时候,虽然知道此方法是浅拷贝,但是在以下代码遇到点不理解,于是顺便理了一下深拷贝与浅拷贝。
let target = { a:1, b: 2 };
let source = { b: 3, c: 4 };
const returnedTarget = Object.assign({},target, source);
returnedTarget.a=5
console.log(target);//{ a: 1, b: 2 }
console.log(returnedTarget);//{ a: 5, b: 3, c: 4 }
傻乎乎以为是浅拷贝,以为target的a也会改为5,结果打脸了。
浅拷贝与深拷贝
简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力。
let a=[1,2,3]
let b=a;
b[0]=0;
console.log(a);//[0, 2, 3]
console.log(b);//[0, 2, 3]
=赋值就是典型的浅拷贝,当改变b的值时候,a的值会跟着改变。
这里就牵扯到基本数据类型与复杂数据类型。
基本数据类型与复杂(引用)数据类型
基本数据类型:number,null,undefined,string,boolean,symbol
复杂数据类型:对象({a:1}),数组:([1,2,3]),函数等。
基本类型-名值存储在栈内存中,例如let a=1;
当b=a时,栈内存会重新开辟一块内存,
当修改b时候,a不会改变,这是因为a与b存储在不同的内存中,但这不算是深拷贝,因为深拷贝本身只针对较为复杂的object类型数据。
引用类型--名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值
当b=a进行拷贝时,其实复制的是a的引用地址,而并非堆里面的值。
而当我们a[0]=1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝了。
那,要是在堆内存中也开辟一个新的内存专门为b存放值,就像基本类型那样,岂不就达到深拷贝的效果了
深拷贝方法
1.自己封装一个深拷贝方法
function deepClone(obj){
let objClone=Array.isArray(obj)?[]:{};
if(obj&&typeof obj==='object'){
for(key in obj){
if(obj.hasOwnProperty(key)){
if(obj[key]&&typeof obj[key]==='object'){
objClone[key]=deepClone(obj[key])
}else{
objClone[key]=obj[key]
}
}
}
}
return objClone;
}
let a=[1,2,3];
let b=deepClone(a);
b[0]=4;
console.log(b,a)
//Array [4, 2, 3] Array [1, 2, 3]
此方法原理为拷贝对象各个层级的属性,每一层都拥有独立的栈内存。
2.使用JSON对象的parse和stringify
function deepClone(obj){
let objString=JSON.stringify(obj);
let objClone=JSON.parse(objString);
return objClone;
}
let a=[1,2,3];
let b=deepClone(a);
b[0]=4;
console.log(b,a);
//Array [4, 2, 3] Array [1, 2, 3]
总结
写到这里也基本了解了深拷贝与浅拷贝,也知道为什么要使用深拷贝,以及怎么实现深拷贝,关于JS内存空间的文章,若有兴趣欢迎阅读 JS 从内存空间谈到垃圾回收机制 这篇文章。