WeackMap
const toProxy = new WeackMap()
注意:WeakMap对Key有限制,它必须是Object(Symbol也不行)
weackMap的方法有 delete、get、has、set。以前还有个clear,后来被弃用了。
var map = new Map();
var weakmap = new WeakMap();
(function INIT(){
var ka = {x: 1};
var kb = {y: 2};
map.set(ka, 'ka');
weakmap.set(kb, 'kb');
})()
map.forEach((val, key) => console.log(key, val))//map可以遍历
// Weakmap. forEach(...) ERROR! //weakmap不能遍历
思考一个很深层的问题,在运行完INIT函数后,还需要在map里保存ka的对象呢?
答案应该是“不保存”:ka和kb的作用域在INIT内,之后我们将无法获取这两个引用,再驻留map里只会产生副作用。但是INIT之后,当遍历map时——map.forEach(…),我们依旧能找到{x: 1},而且除了调用clear方法,我们甚至无法删除这个对象;垃圾回收机制更无法对{x: 1}起作用,久而久之便是内存溢出。
具体原因还是得从Map api中深究。Map api共用了两个数组(一个存放key,一个存放value)。给Map set值时会同时将key和value添加到这两个数组的末尾。从而使得key和value的索引在两个数组中相对应。当从Map取值时,需要遍历所有的key,然后使用索引从存储值的数组中检索出相应的value。
这个实现的缺点很大,首先是赋值和搜索的时间复杂度比较大;其次是可能导致内存溢出,因为数组会一直保存每个键值引用,即便是引用早已离开作用域,垃圾回收器也无法回收这些内存。那WeakMap呢?(虽然就它那几个api,引用不存在后,WeakMap确实也没啥可以操作了)。
var WeakMap = function() {
this.name = '__wm__' + vvid()
};
WeakMap.prototype = {
set: function(key, value) {
Object.defineProperty(key, this.name, {
value: [key, value],
});
return this;
},
get: function(key) {
var entry = key[this.name];
return entry && (entry[0] === key ? entry[1] : undefined);
},
};
weakmap.set(key, val)事实上是直接通过Object.defineProperty给这个key加了一个新属性——this.name,这就解释了为什么WeakMap的key必需是个Object了。
同理,weakmap.get(key)是从key的该属性里获取了值对象。相比Map,WeakMap持有的只是每个键值对的“弱引用”,不会额外开内存保存键值引用。这意味着在没有其他引用存在时,垃圾回收器能正确处理key指向的内存块。正因为这个特殊的实现,WeakMap的key是不可枚举的,更不用说提供keys()、forEach()这类方法了。