浅拷贝
值是基本类型,拷贝的就是基本类型的值
是引用类型,拷贝的就是内存地址,这时就会出现同时改变
let obj={
name:'张三',
age:18,
'身高':180,
'体重':50,
cards:[1,324,354,546,567,567,345]
}
let test=obj
test['age']=20
console.log('obj',obj)
console.log('test',test)
/*
obj
{name: "张三", age: 20, 身高: 180, 体重: 50, cards: Array(7)}
test
{name: "张三", age: 20, 身高: 180, 体重: 50, cards: Array(7)}
*/
浅拷贝的方法还有Object.assign()、es6展开运算符...,针对数组的还有Array.prototype.concat(),Array.prototype.slice()
let obj={
name:'张三',
age:18,
'身高':180,
'体重':50,
cards:[1,324,354,546,567,567,345]
}
let ary=[234,345,45345,7567,568]
let test1=Object.assign({},obj)
let test2={...obj}
let ary1=ary.concat()
let ary2=ary.slice()
test1.age=20
test2.身高=200
console.log(obj)
console.log(test1);
console.log(test2);
console.log('-----------------')
ary1[0]=100
ary2[0]=1000
console.log(ary)
console.log(ary1);
console.log(ary2);
/*
{name: "张三", age: 18, 身高: 180, 体重: 50, cards: Array(7)}
{name: "张三", age: 20, 身高: 180, 体重: 50, cards: Array(7)}
{name: "张三", age: 18, 身高: 200, 体重: 50, cards: Array(7)}
-----------------
(5) [234, 345, 45345, 7567, 568]
(5) [100, 345, 45345, 7567, 568]
(5) [1000, 345, 45345, 7567, 568]
*/
深拷贝
将一个对象从内存中完整拷贝一份,开辟一个新区域存放,即复制对象本身,互不影响
查阅一番,看到最多最简单的是
JSON.parse(JSON.stringify())
试一下
let obj={
name:'张三',
age:18,
'身高':180,
'体重':50,
cards:[1,324,354,546,567,567,345]
}
let test=JSON.parse(JSON.stringify(obj))
test['age']=20
delete test['体重']
test.cards[0]=100
console.log('obj',obj)
console.log('test',test)
/*
obj
{name: "张三", age: 18, 身高: 180, 体重: 50, cards: Array(7)}
name: "张三"
age: 18
身高: 180
体重: 50
cards: Array(7)
0: 1
1: 324
2: 354
3: 546
4: 567
5: 567
6: 345
test
{name: "张三", age: 20, 身高: 180, cards: Array(7)}
name: "张三"
age: 20
身高: 180
cards: Array(7)
0: 100
1: 324
2: 354
3: 546
4: 567
5: 567
6: 345
*/
成功了!但是这个是不合格的,于是我们自己实现一个
const deepClone=(target)=>{
if(typeof target==='object'){
let result={}
for(const key in target){
result[key]=deepClone(target[key])
}
return result
}else{
return target
}
}
/*
obj
{name: "张三", age: 18, 身高: 180, 体重: 50, cards: Array(7)}
name: "张三"
age: 18
身高: 180
体重: 50
cards: Array(7)
0: 1
1: 324
2: 354
3: 546
4: 567
5: 567
6: 345
test
{name: "张三", age: 20, 身高: 180, cards: Object}
name: "张三"
age: 20
身高: 180
cards: Object
0: 100
1: 324
2: 354
3: 546
4: 567
5: 567
6: 345
*/
但是这样没有考虑数组,结果是数组虽然被拷贝了,但是被当作object保存了(for...in遍历出的key,所以看起来数据没有问题),数组直接typeof数组的结果也是'object',所以得另外判断
const deepClone=(target)=>{
if(typeof target==='object'){
let result=Array.isArray(target)?[]:{}
for(const key in target){
result[key]=deepClone(target[key])
}
return result
}else{
return target
}
}
循环引用
如果对象存在循环引用,需要另做处理
const target = {
field1: 1,
field2: undefined,
field3: {
child: 'child'
},
field4: [2, 4, 8]
};
target.target = target;
我们需要额外开辟一个存储空间,来存储当前对象和拷贝对象的关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝
const deepClone=(target,map=new Map())=>{
if(typeof target==='object'){
let result=Array.isArray(target)?[]:{}
if(map.get(target)){
return map.get(target)
}
map.set(target,result)
for(const key in target){
result[key]=deepClone(target[key],map)
}
return result
}else{
return target
}
}
这样就能避免对自身的循环引用。
但是Map是强引用,当拷贝对象非常庞大时会造成内存的巨大消耗,可以用WeakMap代替
此外还可以继续对日期类型、正则、函数进行判断