1.首先我们先区分一下什么是深拷贝、什么是浅拷贝?
假设B复制了A,当修改A时,看B是否发生了变化,若B也跟着变了,那么就说明这是浅拷贝;若B没变,则说明是深拷贝。
2.通过阐述堆栈、基本数据与引用数据来理解深拷贝和浅拷贝。
(1)基本数据类型和引用数据类型的数据存储是不一样的
(2)基本数据类型——名、值都存储在栈内存中。
let a = 1;
当b复制a时,即b = a;栈内存会开辟一个内存空间,此时修改a = 2,对b不会造成影响。但是这也不能算深拷贝,因为深拷贝针对较为复杂的Object类型数据。
(3)引用数据类型——属性名存储在栈内存中,属性值存储在堆内存中,但是栈内存会提供一个引用的地址指向内存中的值。
let a = [0,1];
当b = a,进行复制时,其实复制的是a的引用地址,而并非堆里面的值,当a[0] = 1进行修改时,由于a,b指向的是同一个地址值,b自然也变了,这就是浅拷贝。
若在内存中开辟一个新的内存专门为b存放值,就像基本类型一样,就起到了深拷贝的效果了。
3.简单总结一下:
浅拷贝:只是将对象中的数据引用下来,依旧指向同一个存放地址,拷贝之后的数据修改后,也会影响到原数据中的对象数据。
深拷贝:将对象中所有的数据拷贝下来,对拷贝之后的数据进行修改不会影响到原数据。
4.下面简单介绍一下深拷贝的几种方法:
(1)利用递归进行深拷贝:
function deepClone(obj){
let objectClone = Array.isArray(obj)?[]:{}; //判断obj是否是数组,若是,则是[],若不是,则是{}
if(obj&&typeof obj === 'object'){ //判断obj是否是一个对象,若只用typeof obj === 'object',不能完全判断obj是否是对象,考虑null,null的类型是对象,但它其实并非对象。null转化为数字类型是0,转换为布尔值为false。
for(key in obj){
if(obj.hasOwnProperty(key)){ //判断对象属性里是否包含某个key,key为字符串
if(obj[key]&&typeof obj[key] === 'object'){ //拷贝的对象不为空
objClone[key] = deepClone(obj[key]); //若对象的属性是复杂对象,则继续递归复制
}
else{
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
实现思路:
[1]将要拷贝的数据obj以参数的形式传参。
[2]声明一个变量来存储我们拷贝出来的内容,且判断需要拷贝的是数组还是对象。
[3]判断是否是null,若是,则直接返回。
[4]遍历每个属性,判断每个属性是否是复杂对象,若是,再次递归,若不是,直接复制。
(2)JSON对象实现深拷贝:
function clone(obj){
var copyObj = JSON.stringify(obj); //将对象转换为字符串
var objCopy = JSON.parse(copyObj); /将JSON字符串转换为JSON对象
return objCopy;
}
注意事项:
[1]如果obj里面有RegExp、Error对象,则序列化(JS中的对象转化为字符串)的结果将只得到空对象。
[2]如果obj里面有时间对象,则JSON.Stringify后再JSON.Parse的结果,时间将只是字符串,而不是时间对象。
[3]如果obj里面有函数、undefined,则序列化的结果会把undefined丢失。
[4]如果obj里面有NaN、infinity和-infinity,则序列化的结果会变成null。
[5]无法处理function,无法处理循环引用对象。
//循环引用对象
var a = {'name':'zzz'};
var b = {'name':'vvv'};
a.child = b;
b.parent = a;
//注意:这里的a和b都是循环引用对象,JSON.stringify()对该类对象序列化会报错
(3)使用jQuery(extend)方法实现深拷贝:
var obj = {
a:10,
b:function(){
console.log(this.a)
}
}
var newObj = $.extend(true,{},obj);
注意:$.extend中的true为深拷贝,false为浅拷贝,其实是把第二个对象上面的属性和对象覆盖到第一个对象上。
(4)使用Object.assign()实现深拷贝:
var obj = {a:0,b:{c:0}}
var obj2 = Object.assign({},obj1)
注意:当对象中只有一级属性,没有二级属性,此方法为深拷贝,但是对象中有对象时,此方法在二级属性后为浅拷贝。