ES6 中对 WeakMap 的要求主要是对 key(非 null 对象) 仅保留弱引用,且取值是 O(1) 复杂度,这导致了它的两个特性:

  • 不能求大小,更不能枚举。
  • 当 key 对象不被其他变量引用时,会被垃圾回收系统回收。

按说这种关乎内存管理的对象是没法 shim 的。但是webcomponents.js 对 WeakMap 有一个很奇妙的 shim。经过阅读后整理原理如图:

WeakMap shim 原理

解说:

  1. WeakMap 内不提供存储 key 和 value 的区域,避免产生本对象对 key 的强引用。
  2. 每个 WeakMap 中只存储一个随机、唯一生成的 name 字符串。
  3. 在 WeakMap 中的每一个 key 中设置一个属性:
  • 属性名是 WeakMap 的唯一 name。
  • 值为该 key 在 WeakMap 中对应的 value。
  1. 此时,获取 WeakMap 中指定 key 对应的 value,就等价于获取 key 中的 WeakMap::name 属性的值。
  2. 为了防止由于原型继承导致 WeakMap::name 属性不是本对象自有的属性,采用了一个技巧来保证获取 WeakMap::name 属性的值不是从原型继承来的,这个技巧作为另一个文章再谈。
  • 虽然使用 Object::hasOwnProperty 也可以达到相同的效果,但是从库的级别考虑,该属性可能被第三方代码污染,所以采用了更极端的方式来验证。

通过这种引用方式,达成了 WeakMap 对象事实上没有引用到 key 对象的效果,那么当 key 对象不被别的变量引用时,就会被垃圾回收系统自动回收。同时取值也是 O(1) 复杂度的。