You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionWeakMap(){/** Implementation of harmony `WeakMaps`, in ES5\. This implementation will work only with keys that have configurable `valueOf` property (which is a default for all non-frozen objects). http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps **/varprivates=Name()returnObject.freeze(Object.create(WeakMap.prototype,{has: {value: functionhas(object){return'value'inprivates(object)},configurable: true,enumerable: false,writable: true},get: {value: functionget(key,fallback){returnprivates(guard(key)).value||fallback},configurable: true,enumerable: false,writable: true},set: {value: functionset(key,value){privates(guard(key)).value=value},configurable: true,enumerable: false,writable: true},'delete': {value: functionset(key){returndeleteprivates(guard(key)).value},configurable: true,enumerable: false,writable: true}}))}
functiondefineNamespace(object,namespace){/** Utility function takes `object` and `namespace` and overrides `valueOf` method of `object`, so that when called with a `namespace` argument, `private` object associated with this `namespace` is returned. If argument is different, `valueOf` falls back to original `valueOf` property. **/// Private inherits from `object`, so that `this.foo` will refer to the// `object.foo`. Also, original `valueOf` is saved in order to be able to// delegate to it when necessary.varprivates=Object.create(object),base=object.valueOfObject.defineProperty(object,'valueOf',{value: functionvalueOf(value){// If `this` or `namespace` is not associated with a `privates` being// stored we fallback to original `valueOf`, otherwise we return privates.returnvalue!=namespace||this!=object ? base.apply(this,arguments)
: privates},configurable: true})returnprivates}functionName(){/** Desugared implementation of private names proposal. API is different as it's not possible to implement API proposed for harmony with in ES5\. In terms of provided functionality it supposed to be same. http://wiki.ecmascript.org/doku.php?id=strawman:private_names **/varnamespace={}returnfunctionname(object){varprivates=object.valueOf(namespace)returnprivates!==object ? privates : defineNamespace(object,namespace)}}functionguard(key){/** Utility function to guard WeakMap methods from keys that are not a non-null objects. **/if(key!==Object(key))throwTypeError("value is not a non-null object")returnkey}
ECMAScript 6中加入了很多新的特性,其中有一个有用的API:WeakMap。Nicholas的博文做了详细的介绍。这也是一篇关于WeakMap的笔记。
简介
WeakMap
与Map
(另一个ES6的新API)都是键值对象,有着类似的API:set
、get
、has
和delete
。为了更好的解释WeakMap
,先谈下Javascript中的键值对象。JS中几乎所有可写的对象就可以当作一个键值对象,例如:但是这样简单的键值对象达不到一个效果:它的键“不能”是对象。之所以打引号是因为并不是不能用对象,而是对象会转换成字符串,如下:
于是ES6就提供了
Map
与WeakMap
的API,可以实现键是对象的情况。但是这引来了另外一个问题,Map
是可以通过keys
方法来获取其所有的键。这就需要map保留一个对键的强引用,于是就导致了引用释放上的不方便,稍微大意一些就容易造成内存泄漏。于是又提供了
WeakMap
,它的键只能是一个非空(null)对象,而Map
的键则还可以是除对象外的原始类型。WeakMap
的重要特点在于:对键的引用是弱引用,而不是一般的强引用。看如下例子:WeakMap
的键只能是非空(null)对象,所以如果你在get
方法里面传入null或是undefined之类的值,就会报错:“value is not a non-null object”。它的另外一个特点就是无法获取所有的键,更无法获取size之类的值。WeakMap
一个典型的应用场景就是对于jQuery这样的库,需要维护一个dom列表,存储对应每个dom的数据。这时候使用它就可以达到“dom在文档中被移除的时候,自动释放dom对象”。当然ES6目前还没有普及,支持的浏览器仅有Firefox和Chrome。在chrome中你需要到
chrome://flags
,并且启用“Experimental JavaScript Features”。所以我们需要fallback。Fallback
Gozala提供了一个很精妙的fallback方案!接下来对它的代码做解读。要实现WeakMap有几个关注点:
因为这个方案是基于ECMAScript 5的,所以使用了
Object.freeze
和Object.create
之类的API。WeakMap的初始化函数如下:使用了
Object.create
创建了一个继承自WeakMap的新对象,并设置了has,get,set,delete方法。(其实我不太理解delete是个关键字,为什么标准要把它作为WeakMap的方法名之一?)然后使用Object.freeze
确保对象无法被改变。避免有人通过map.a = 'string'
这样或其他方式去修改对象。其中的关键点在于
var privates = Name()
一句。privates方法是用来“依据键获取键值”。因为不能保存键的强引用所以需要将键值保存在键上。即WeakMap仅仅是提供了一个“键”与“值”之间的接口,通过WeakMap来设置或获取键对应值。这么一来就容易理解了,具体源代码如下:
代码中guard方法用于验证键是否是非空对象,又通过重写valueOf方法来获取键对应的存储值的对象。即是如下的引用关系:
其中的关键点是namespace是一个空对象。为什么呢?因为一个空对象是无法模仿的、是唯一的,如果改用字符串则很容易被知道字符串值的人直接通过
key.valueOf('namespace')
方法获取到privates。所以实现WeakMap的核心是“如何构造一个唯一的无法伪造的属性名”。
Nicolas还在Gozala方案的评论中提到了另外一种实现,具体见http://code.google.com/p/es-lab/source/browse/trunk/src/ses/WeakMap.js。我还没有仔细看~
The text was updated successfully, but these errors were encountered: