WeakSet

“弱集合” 是一种新的集合引用类型,

基本 API

可以使用 new 关键字实例化一个空的 WeakSet:

const ws = new WeakSet();

弱集合中的值只能是 Object 或者继承自 Object 的类型,尝试使用非对象设置值会抛出 TypeError。

如果想在初始化时填充弱集合,则构造函数可以接收一个可迭代对象,其中需要包含有效值。可迭代对象中的每个值都会按照迭代顺序插入到新实例中:

const val1 = { id: 1 },
  val2 = { id: 2 },
  val3 = { id: 3 };

// 使用数组初始化弱集合
const ws1 = new WeakSet([val1, val2, val3]);

console.log(ws1.has(val1)); // true
console.log(ws1.has(val2)); // true
console.log(ws1.has(val3)); // true

// 初始化是全有或全无的操作
// 只要有一个值无效就会抛出错误,导致整个初始化失败
const ws2 = new WeakSet([val1, "BADVAL", val3]);
// TypeError

// 原始值可以先包装成对象再用作值
const stringVal = new String("val1");
const ws3 = new WeakSet([stringKey]);
console.log(ws3.has(stringVal)); // true

初始化之后,可以使用 add() 再添加新值,可以使用 has() 查询,还可以使用 delete() 删除:

const ws = new WeakSet();

const val1 = { id: 1 },
  val2 = { id: 2 };

ws.add(val1).add(val2);

console.log(ws.has(val1)); // true
console.log(ws.has(val2)); // true

ws.delete(val1); // 删除

console.log(ws.has(val1)); // false
console.log(ws.has(val2)); // true

弱值

WeakSet 中 “weak” 表示弱集合的值是 “弱弱地拿着” 的。意思就是,这些值不属于正式的引用,不会阻止垃圾回收。

const ws = new WeakSet();
ws.add({});

add() 方法初始化了一个新对象,并将它用作一个值。因为没有指向这个对象的其他引用,所以当这行代码执行完成后,这个对象值就会被当做垃圾回收。然后,这个值就从弱集合中消失了,使其成为一个空集合。

const ws = new WeakSet();

const container = {
  val: {},
};

ws.add(container.val);

function removeReference() {
  container.val = null;
}

这一次,container 对象维护着一个对弱集合值的引用,因此这个对象值不会成为垃圾回收的目标。不过,如果调用了 removeReference(),就会摧毁值对象的最后一个引用,垃圾回收程序就可以把这个值清理掉。

不可迭代值

因为 WeakSet 中的值在任何时候都可能被销毁,所以没必要提供迭代其值的能力。当然,也用不着像 clear() 这样一次性销毁所有值的方法。WeakSet 确实也没有这个方法。因为不可以迭代,所以也不可能在不知道对象引用的情况下从弱集合中取得值。即便代码可以访问 WeakSet 实例,也没办法看到其中的内容。

WeakSet 之所以限制只能用对象作为值,是为了保证只有通过值对象的引用才能取得值。

使用弱集合

与 WeakMap 类似,WeakSet 适合在不妨碍垃圾回收的情况下跟踪对象,

const disabledElements = new Set();
const loginBtn = document.getElementById("login");
disabledElements.add(loginBtn);

这样通过查询元素在不在 disabledElements 中,就可以知道它是不是被禁用了。不过,假如元素从 DOM 树中被删除了,它的引用却仍然保存在 Set 中,因此垃圾回收程序也不能回收它。

为了让垃圾回收程序回收元素的内存,可以在这里使用 WeakSet。

const disabledElements = new WeakSet();
const loginBtn = document.getElementById("login");
disabledElements.add(loginBtn);

这样,只要 WeakSet 中任何元素从 DOM 树中被删除,垃圾回收程序就可以忽略其存在,而立即释放其内存(假设没有其他地方引用这个对象)。