其次, WeakSet 中的对象都是弱引用, 即垃圾回收机制不考虑 WeakSet 对该对象的引用, 也就是说, 如果其他对象都不再引用该对象, 那么垃圾回收机制会自动回收该对象所占用的内存, 不考虑该对象还存在于 WeakSet 之中。 这个特点意味着, 无法引用 WeakSet 的成员, 因此 WeakSet 是不可遍历的。
var ws = new WeakSet(); ws.add(1) // TypeError: Invalid value used in weak set ws.add(Symbol()) // TypeError: invalid value used in weak set上面代码试图向 WeakSet 添加一个数值和Symbol值, 结果报错, 因为 WeakSet 只能放置对象。 WeakSet 是一个构造函数, 可以使用new命令, 创建 WeakSet 数据结构。 var ws = new WeakSet();作为构造函数, WeakSet 可以接受一个数组或类似数组的对象作为参数。( 实际上, 任何具有 iterable 接口的对象, 都可以作为 WeakSet 的参数。) 该数组的所有成员, 都会自动成为 WeakSet 实例对象的成员。 var a = [ [1, 2], [3, 4] ]; var ws = new WeakSet(a);上面代码中, a是一个数组, 它有两个成员, 也都是数组。 将a作为 WeakSet 构造函数的参数, a的成员会自动成为 WeakSet 的成员。 注意, 是a数组的成员成为 WeakSet 的成员, 而不是a数组本身。 这意味着, 数组的成员只能是对象。 var b = [3, 4]; var ws = new WeakSet(b); // Uncaught TypeError: Invalid value used in weak set(…)上面代码中, 数组b的成员不是对象, 加入 WeaKSet 就会报错。 WeakSet 结构有以下三个方法。 WeakSet.prototype.add(value): 向 WeakSet 实例添加一个新成员。 WeakSet.prototype.delete(value): 清除 WeakSet 实例的指定成员。 WeakSet.prototype.has(value): 返回一个布尔值, 表示某个值是否在 WeakSet 实例之中。 下面是一个例子。 var ws = new WeakSet(); var obj = {}; var foo = {}; ws.add(window); ws.add(obj); ws.has(window); // true ws.has(foo); // false ws.delete(window); ws.has(window); // false WeakSet 没有size属性, 没有办法遍历它的成员。 ws.size // undefined ws.forEach // undefined ws.forEach(function(item) { console.log('WeakSet has ' + item) }) // TypeError: undefined is not a function上面代码试图获取size和forEach属性, 结果都不能成功。 WeakSet 不能遍历, 是因为成员都是弱引用, 随时可能消失, 遍历机制无法保证成员的存在, 很可能刚刚遍历结束, 成员就取不到了。 WeakSet 的一个用处, 是储存 DOM 节点, 而不用担心这些节点从文档移除时, 会引发内存泄漏。 下面是 WeakSet 的另一个例子。 const foos = new WeakSet() class Foo { constructor() { foos.add(this) } method() { if(!foos.has(this)) { throw new TypeError('Foo.prototype.method 只能在 Foo 的实例上调用! '); } } }上面代码保证了Foo的实例方法, 只能在Foo的实例上调用。 这里使用 WeakSet 的好处是, foos对实例的引用, 不会被计入内存回收机制, 所以删除实例的时候, 不用考虑foos, 也不会出现内存泄漏。