深拷贝相对浅拷贝而言,如果遇到属性值为引用类型的时候,它新建一个引用类型并将对应的值复制给它,因此对象获得的一个新的引用类型而不是一个原有类型的引用。深拷贝对于一些对象可以使用 JSON 的两个函数来实现,但是由于 JSON 的对象格式比 js 的对象格式更加严格,所以如果属性值里边出现函数或者 Symbol 类型的值时,会转换失败
(1)JSON.stringify()
- JSON.parse(JSON.stringify(obj))是目前比较常用的深拷贝方法之一,它的原理就是利用JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象。
- 这个方法可以简单粗暴的实现深拷贝,但是还存在问题,拷贝的对象中如果有函数,undefined,symbol,当使用过JSON.stringify()进行处理之后,都会消失。
(2)函数库lodash的_.cloneDeep方法
该函数库也有提供_.cloneDeep用来做 Deep Copy
(3)手写实现深拷贝函数
步骤:
- 先判断map中是否已经存在(已经拷贝过了),存在则返回,防止循环引用(进入死循环会导致栈内存溢出)
- 判断(instanceof)当前类型是不是Object,如果不是直接返回target
- 如果是,再判断其是不是数组,函数,正则(RegExp),日期,或者是普通对象,对应的创建不同类型的数据
- 数组:newobj=[]
- 函数:newobj=function(){return target.call(this,...arguments)}
- 正则:newobj=new RegExp(target.source,target.flags)
- 日期:newobj=new Date(target)
- 普通对象:newobj={}
4,加入map进行标记 map.set(target,newobj)
5,遍历target的属性,如果是自身的属性,则递归赋值给新对象 newobj[key]=dd(target[key],map)
6,返回新对象newobj
function dd(target,map=new WeakMap()){
//循环
if(map.get(target)){
return map.get(target)
}
if(target instanceof Object){
let newobj;
if(target instanceof Array){
newobj=[]
}else if(target instanceof Function){
newobj=function(){
return target.call(this,...arguments)
}
}else if(target instanceof RegExp){
newobj=new RegExp(target.source,target.flags)
}else if(target instanceof Date){
newobj=new Date(target)
}else{
newobj={}
}
//标记
map.set(target,newobj)
for(let key in target){
if(target.hasOwnProperty(key)){
newobj[key]=dd(target[key],map)
}
}
return newobj
}else{
return target
}
}
let obj={
a:function(){
console.log('a')
},
b:{
c:'d'
},
d:[1,2,3,4]
}
let r=dd(obj)
console.log(obj.a())
console.log(r)
关于weakMap:
WeakMap
的作用:
WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。
什么是弱引用呢?
在计算机程序设计中,弱引用与强引用相对,是指不能确保其引用的对象不会被垃圾回收器回收的引用。 一个对象若只被弱引用所引用,则被认为是不可访问(或弱可访问)的,并因此可能在任何时刻被回收。
我们默认创建一个对象:const obj = {}
,就默认创建了一个强引用的对象,我们只有手动将obj = null
,它才会被垃圾回收机制进行回收,如果是弱引用对象,垃圾回收机制会自动帮我们回收。
举个例子:
如果我们使用Map
的话,那么对象间是存在强引用关系的:
let obj = { name : 'ConardLi'}
const target = new Map();
target.set(obj,'code秘密花园');
obj = null;
复制代码
虽然我们手动将obj
,进行释放,然是target
依然对obj
存在强引用关系,所以这部分内存依然无法被释放。
再来看WeakMap
:
let obj = { name : 'ConardLi'}
const target = new WeakMap();
target.set(obj,'code秘密花园');
obj = null;
复制代码
如果是WeakMap
的话,target
和obj
存在的就是弱引用关系,当下一次垃圾回收机制执行时,这块内存就会被释放掉。
设想一下,如果我们要拷贝的对象非常庞大时,使用Map
会对内存造成非常大的额外消耗,而且我们需要手动清除Map
的属性才能释放这块内存,而WeakMap
会帮我们巧妙化解这个问题。