ES6 中对 WeakMap 的要求主要是对 key(非 null 对象) 仅保留弱引用,且取值是 O(1) 复杂度,这导致了它的两个特性:
- 不能求大小,更不能枚举。
- 当 key 对象不被其他变量引用时,会被垃圾回收系统回收。
按说这种关乎内存管理的对象是没法 shim 的。但是webcomponents.js 对 WeakMap 有一个很奇妙的 shim。经过阅读后整理原理如图:
解说:
- WeakMap 内不提供存储 key 和 value 的区域,避免产生本对象对 key 的强引用。
- 每个 WeakMap 中只存储一个随机、唯一生成的 name 字符串。
- 在 WeakMap 中的每一个 key 中设置一个属性:
- 属性名是 WeakMap 的唯一 name。
- 值为该 key 在 WeakMap 中对应的 value。
- 此时,获取 WeakMap 中指定 key 对应的 value,就等价于获取 key 中的 WeakMap::name 属性的值。
- 为了防止由于原型继承导致 WeakMap::name 属性不是本对象自有的属性,采用了一个技巧来保证获取 WeakMap::name 属性的值不是从原型继承来的,这个技巧作为另一个文章再谈。
- 虽然使用 Object::hasOwnProperty 也可以达到相同的效果,但是从库的级别考虑,该属性可能被第三方代码污染,所以采用了更极端的方式来验证。
通过这种引用方式,达成了 WeakMap 对象事实上没有引用到 key 对象的效果,那么当 key 对象不被别的变量引用时,就会被垃圾回收系统自动回收。同时取值也是 O(1) 复杂度的。