[译]ECMAScript 6中的集合类型,第三部分:WeakMap

原文:http://www.nczonline.net/blog/2012/11/06/ecmascript-6-collections-part-3-weakmaps/


WeakMap类似于常规的Map的一点是,它们都是把一个值映射到某个唯一的键上,然后就可以使用这个键获取到与之对应的值.WeakMapMap不同的地方是,它的键只能是对象值而不可以是原始值.虽然这个限制看起来很奇怪,但正是这一点,才让WeakMap变得很有价值.

一个WeakMap对象的键只持有其所引用对象的弱引用,弱引用的特点是,它不能阻止垃圾回收器回收其引用的对象.当那个对象被垃圾回收器销毁后,WeakMap对象中引用它的那个键值对也会被删除.使用WeakMap最典型的例子就是用在要创建一个与特定的DOM元素相关联的对象的时候.例如,jQuery在程序内部维护着一些对象的缓存,每个缓存引用着一个DOM元素.使用WeakMap的话,jQuery就可以在某些DOM元素被从文档中删除后就自动释放它们先前占用的内存.

ECMAScript 6中的WeakMap类型是一个无序的键值对列表,键必须是一个非null的对象(non-null object),值可以是任意类型.WeakMap的API很简单,和Map一样,set()get()分别用来添加数据和获取数据:

var map = new WeakMap(),
element
= document.querySelector(".element");

map.set(element,
"Original");

// 下面就可以使用了
var value = map.get(element);
console.log(value);
// "Original" // 下面删除引用 element.parentNode.removeChild(element);
element
= null;

//译者注:下面这句实际上会报错(value is not a non-null object),因为键必须是非null的对象.作者只是为了讲解,看下面.
value
= map.get(element);
console.log(value);
// undefined

在这个例子中,我们存储了一个键值对.键是一个DOM元素,存储着一个对应的字符串值.随后把那个DOM元素传入get()方法来获取到存储的字符串值.如果这个DOM元素被从文档中删除了,指向它的变量也被赋值为null,然后WeakMap对象中的那个键值对会被自动删除,这时如果再读取那个键值对的话,就会失败.

这个例子有点误导,因为在第二次调用map.get(element)的时候,实际上是把null(element被赋的值)而不是DOM元素的引用传进去了.你不能使用null来作为WeakMap对象的键(WeakMap的键只能是对象,null不是对象),这样的代码是会抛出异常的,根本不会执行到console.log语句.上面的代码之所以那么写,只是为了讲解.想要说明那个键值对已经不存在了.不过不幸的是,我们没有任何办法来检测这个键值对是否真的被删除了(因为element的值已经是null,我们无法获取到指向那个DOM元素的引用了,假如真能获取到那个DOM对象的引用,传给get方法的话,应该返回undefined).

译者注:由于语文水平实在有限,上面的这段话我无法表达清楚.所以我用我拙劣的水平做了几张图,希望我的理解是对的,也希望你能看懂.

var map = new WeakMap(),
element
= document.querySelector(".element");

变量map和element各自引用了一个对象.

[译]ECMAScript 6中的集合类型,第三部分:WeakMap_第1张图片

map.set(element, "Original");
map中添加了一个键值对,这个键和变量element同时指向一个DOM对象,只是一个是强引用,一个是弱引用.

[译]ECMAScript 6中的集合类型,第三部分:WeakMap_第2张图片

element.parentNode.removeChild(element);
element
= null;

强引用全部主动断掉,最后剩下的弱引用也自动断掉,孤立的DOM元素被回收,WeakMap对象中对应的键值对被自动清除.如果是常规的Map,由于不是弱引用,所以那个DOM对象不会被回收,仍然存在,仍然可以通过get()方法访问到.

[译]ECMAScript 6中的集合类型,第三部分:WeakMap_第3张图片

WeakMap对象还有一个has()方法,可以判断某个对象引用是否作为了自己的键,还有delete()方法,用来删除一个键值对.

var map = new WeakMap(),
element
= document.querySelector(".element");

map.set(element,
"Original");

console.log(map.has(element));
// true console.log(map.get(element)); // "Original" map.delete(element);
console.log(map.has(element));
// false console.log(map.get(element)); // undefined

使用delete()方法删除某个键后,再执行has()方法的时候会返回false ,执行get()方法会返回undefined.

浏览器支持

Firefox和Chrome都已经实现了WeakMap,可是,在Chrome中,你必须手动开启ECMAScript 6特性:打开chrome://flags勾选"启用实验性 JavaScript".目前浏览器的实现都完全遵循了当前的strawman[1]规范(而ECMAScript 6的最新草案新添加了一个WeakMap.prototype.clear()方法,这两个浏览器目前还都没实现).

用途和限制

目前WeakMap有一个特定的使用情境,那就是在将值映射到一些未来可能被会删除的对象上的时候.WeakMap的这种"能释放某些无用对象所占用的内存的能力"对于那种将DOM元素包装成为某种自定义对象的JavaScript库来说是很有用的(比如jQuery和YUI).在未来,WeakMap的标准完全实现且被广泛使用之后,应该会有更多的能用到WeakMap的地方,所以在短期内,不要因为想不到使用WeakMap的好点子而苦恼.

在大多数情况下,使用常规的Map应该是你最合适的选择.WeakMap有不少限制,比如不能枚举键值对(for of),也不能得知它们到底包含了多少个键值对(size).如果你需要这样做,那么就使用常规的Map.如果你仅仅需要把一些对象作为键,且不需要其他更多的功能,那么WeakMap是个好的选择.

参考

  1. WeakMapsStrawman (ECMA)

你可能感兴趣的:(ECMAScript)