ES6 系列之 WeakMap的使用示例
前言
我们先从WeakMap的特性说起,然后聊聊WeakMap的一些应用场景。
特性
1.WeakMap只接受对象作为键名
constmap=newWeakMap(); map.set(1,2); //TypeError:Invalidvalueusedasweakmapkey map.set(null,2); //TypeError:Invalidvalueusedasweakmapkey
2.WeakMap的键名所引用的对象是弱引用
这句话其实让我非常费解,我个人觉得这句话真正想表达的意思应该是:
WeakMapshold"weak"referencestokeyobjects,
翻译过来应该是WeakMaps保持了对键名所引用的对象的弱引用。
我们先聊聊弱引用:
在计算机程序设计中,弱引用与强引用相对,是指不能确保其引用的对象不会被垃圾回收器回收的引用。一个对象若只被弱引用所引用,则被认为是不可访问(或弱可访问)的,并因此可能在任何时刻被回收。
在JavaScript中,一般我们创建一个对象,都是建立一个强引用:
varobj=newObject();
只有当我们手动设置obj=null的时候,才有可能回收obj所引用的对象。
而如果我们能创建一个弱引用的对象:
//假设可以这样创建一个 varobj=newWeakObject();
我们什么都不用做,只用静静的等待垃圾回收机制执行,obj所引用的对象就会被回收。
我们再来看看这句:
WeakMaps保持了对键名所引用的对象的弱引用
正常情况下,我们举个例子:
constkey=newArray(5*1024*1024); constarr=[ [key,1] ];
使用这种方式,我们其实建立了arr对key所引用的对象(我们假设这个真正的对象叫Obj)的强引用。
所以当你设置key=null时,只是去掉了key对Obj的强引用,并没有去除arr对Obj的强引用,所以Obj还是不会被回收掉。
Map类型也是类似:
letmap=newMap(); letkey=newArray(5*1024*1024); //建立了map对key所引用对象的强引用 map.set(key,1); //key=null不会导致key的原引用对象被回收 key=null;
我们可以通过Node来证明一下这个问题:
//允许手动执行垃圾回收机制 node--expose-gc global.gc(); //返回Nodejs的内存占用情况,单位是bytes process.memoryUsage();//heapUsed:4640360≈4.4M letmap=newMap(); letkey=newArray(5*1024*1024); map.set(key,1); global.gc(); process.memoryUsage();//heapUsed:46751472注意这里大约是44.6M key=null; global.gc(); process.memoryUsage();//heapUsed:46754648≈44.6M //这句话其实是无用的,因为key已经是null了 map.delete(key); global.gc(); process.memoryUsage();//heapUsed:46755856≈44.6M
如果你想要让Obj被回收掉,你需要先delete(key)然后再key=null:
letmap=newMap(); letkey=newArray(5*1024*1024); map.set(key,1); map.delete(key); key=null;
我们依然通过Node证明一下:
node--expose-gc global.gc(); process.memoryUsage();//heapUsed:4638376≈4.4M letmap=newMap(); letkey=newArray(5*1024*1024); map.set(key,1); global.gc(); process.memoryUsage();//heapUsed:46727816≈44.6M map.delete(key); global.gc(); process.memoryUsage();//heapUsed:46748352≈44.6M key=null; global.gc(); process.memoryUsage();//heapUsed:4808064≈4.6M
这个时候就要说到WeakMap了:
constwm=newWeakMap(); letkey=newArray(5*1024*1024); wm.set(key,1); key=null;
当我们设置wm.set(key,1)时,其实建立了wm对key所引用的对象的弱引用,但因为letkey=newArray(5*1024*1024)建立了key对所引用对象的强引用,被引用的对象并不会被回收,但是当我们设置key=null的时候,就只有wm对所引用对象的弱引用,下次垃圾回收机制执行的时候,该引用对象就会被回收掉。
我们用Node证明一下:
node--expose-gc global.gc(); process.memoryUsage();//heapUsed:4638992≈4.4M constwm=newWeakMap(); letkey=newArray(5*1024*1024); wm.set(key,1); global.gc(); process.memoryUsage();//heapUsed:46776176≈44.6M key=null; global.gc(); process.memoryUsage();//heapUsed:4800792≈4.6M
所以WeakMap可以帮你省掉手动删除对象关联数据的步骤,所以当你不能或者不想控制关联数据的生命周期时就可以考虑使用WeakMap。
总结这个弱引用的特性,就是WeakMaps保持了对键名所引用的对象的弱引用,即垃圾回收机制不将该引用考虑在内。只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。
也正是因为这样的特性,WeakMap内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此ES6规定WeakMap不可遍历。
所以WeakMap不像Map,一是没有遍历操作(即没有keys()、values()和entries()方法),也没有size属性,也不支持clear方法,所以WeakMap只有四个方法可用:get()、set()、has()、delete()。
应用
1.在DOM对象上保存相关数据
传统使用jQuery的时候,我们会通过$.data()方法在DOM对象上储存相关信息(就比如在删除按钮元素上储存帖子的ID信息),jQuery内部会使用一个对象管理DOM和对应的数据,当你将DOM元素删除,DOM对象置为空的时候,相关联的数据并不会被删除,你必须手动执行$.removeData()方法才能删除掉相关联的数据,WeakMap就可以简化这一操作:
letwm=newWeakMap(),element=document.querySelector(".element"); wm.set(element,"data"); letvalue=wm.get(elemet); console.log(value);//data element.parentNode.removeChild(element); element=null;
2.数据缓存
从上一个例子,我们也可以看出,当我们需要关联对象和数据,比如在不修改原有对象的情况下储存某些属性或者根据对象储存一些计算的值等,而又不想管理这些数据的死活时非常适合考虑使用WeakMap。数据缓存就是一个非常好的例子:
constcache=newWeakMap(); functioncountOwnKeys(obj){ if(cache.has(obj)){ console.log('Cached'); returncache.get(obj); }else{ console.log('Computed'); constcount=Object.keys(obj).length; cache.set(obj,count); returncount; } }
3.私有属性
WeakMap也可以被用于实现私有变量,不过在ES6中实现私有变量的方式有很多种,这只是其中一种:
constprivateData=newWeakMap(); classPerson{ constructor(name,age){ privateData.set(this,{name:name,age:age}); } getName(){ returnprivateData.get(this).name; } getAge(){ returnprivateData.get(this).age; } } exportdefaultPerson;
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。