一、如何简单区分深拷贝与浅拷贝
用语言描述,假设B复制了A,然后修改A,当修改A时,B发生了变化,说明这就是浅拷贝,若是B没有发生变化,就是深拷贝
二、JS基本数据类型与与引用数据类型数据存储
1、基本数据类型数据存储(number,string,boolean,null,undefined,symbol,BigInt)
基本类型的 名和值都存储在栈内存中
当你使用b= a 进行复制时,栈内存会开辟一个新的内存
所以当此时我们对a进行修改值的时候,b的值并不会发生改变,这就相当于深拷贝,但也并不算深拷贝,因为深拷贝是针对较为复杂的object类型数据
2、引入数据类型的数据存储
名 存在在栈内存中,值 存在于堆内存中,但是栈内存会提供一个引用的地址类型指向堆内存中的值
因此当我们使用b=a,进行拷贝的时候,复制的是a的引用地址,而并非堆里面的值
所以当我们使用a[0] = 1 的时候,由于a与b指向的是一个地址,所以b的值也会发生改变,这就是浅拷贝
思考:如果我们像基本数据类型一样,在堆内存中也开辟一个新的内存专门用来存放值,就可以达到深拷贝的效果
三、实现深拷贝的几种方法
1、使用递归去复制所有的层级属性
function deepClone(obj){
//Array.isArray(obj)用于确定传递的值obj是否是一个数组
let objClone = Array.isArray(obj)?[]:{};
//typeof操作符用于返回一个字符串,表示未经计算的操作数的类型
if(obj && typeof obj==="object"){
//for ... in 用于对数组或者对象的属性进行循环操作
for(key in obj){
//hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key] && typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[1,2,3,4],
b=deepClone(a);
a[0]=2;
console.log(a,b);
let a = {ww:'12',qw:'33'}
b=deepClone(a)
a.ww = 'dd'
console.log(a,b);
注意:slice()方法并不是深拷贝
这个看似像是深拷贝
let a=[1,2,3,4],
b=a.slice();
a[0]=2;
console.log(a,b);
再试试这个,就会出现错误,深拷贝是会拷贝所有层的属性,而slice()仅仅是一层属性
let a=[0,1,[2,3],4]
//array.slice(start,end) 从已有的数组中返回选定的元素
b=a.slice();
a[0]=1;
//代表第3列第1行
a[2][0]=1;
console.log(a,b)
2、JSON对象的parse和stringify
function deepClone(obj){
//JSON.stringify()把javaScript对象转换为字符串(序列化)
//序列化的作用是存储(因为引用类型本身存储的仅仅只是一个地址,序列化后,会将引用类型换成字符串的类型然后可以在栈中保存值)
//序列化注意事项:
// 1、如果obj中有时间对象,则JSON.stringfy之后再JSON.parse的结果,时间将只是字符串的形式,而不是时间对象
// 2、如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象;
// 3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
// 4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
// 5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
// 6、如果对象中存在循环引用的情况也无法正确实现深拷贝;
let _obj = JSON.stringify(obj)
//JSON.parse()将数据转换为JS对象
objClone = JSON.parse(_obj);
return objClone
}
let a=[0,1,[2,3],4],
b=deepClone(a);
a[0]=1;
a[2][0]=1;
console.log(a,b);