WeakSet 只能存储对象且不阻止垃圾回收,仅提供 add()、delete()、has() 方法,适用于对象身份标记等无需影响对象生命周期的场景。

WeakSet 只能存对象,且不阻止垃圾回收
WeakSet 是 JavaScript 中一种特殊的集合类型,它的核心特性是「弱引用」:只要一个对象只被 WeakSet 引用,而没有其他变量或结构持有它,这个对象就会被垃圾回收器正常回收。这意味着 WeakSet 不会阻止内存释放,也不会暴露内部元素数量或提供遍历方法(如 forEach、values()),也不能用 for...of 循环。
它只提供三个方法:add()、delete()、has(),且所有参数必须是对象(传入原始值会直接报错 TypeError: Invalid value used in weak set)。
适合做“对象身份标记”,比如防重复处理或临时状态追踪
WeakSet 常用于需要临时记录某些对象是否“已被处理过”但又不想影响其生命周期的场景。典型例子包括:
- 在递归遍历 DOM 或嵌套对象时,避免循环引用导致栈溢出 —— 把已访问过的对象存在 WeakSet 里,每次进入前
has()判断 - 为第三方对象打标记(比如给某个
element标记“已绑定事件”),不侵入对象自身属性,也不担心内存泄漏 - 实现私有状态容器:配合闭包,把实例对象作为 key 存进 WeakSet,外部无法访问该集合,也无法通过反射获取标记对象
注意:不能用 WeakSet 存字符串、数字、null 或 undefined,哪怕包装成对象(如 new String('a'))也不推荐——语义不清且容易误判。
立即学习“Java免费学习笔记(深入)”;
WeakSet 和 Set 的关键区别不只是“弱引用”
除了引用强度不同,它们在能力上也有硬性差异:
-
WeakSet没有size属性,无法知道存了多少个对象 -
WeakSet不可迭代,没有keys()、values()、entries()方法 -
WeakSet不能用数组或任何可迭代对象初始化(构造函数只接受undefined或另一个 WeakSet) -
Set允许存储任意类型值;WeakSet只接受对象,且拒绝代理对象(Proxy)、WeakMap等部分内置对象(取决于引擎实现,但行为不保证)
性能上,WeakSet 的 has() 和 add() 通常是 O(1),但底层实现依赖引擎优化,不建议用于高频判断逻辑(比如每帧调用上百次),优先考虑更可控的数据结构。
一个真实可用的 DOM 事件防重绑示例
下面这段代码演示如何用 WeakSet 避免给同一个 DOM 元素重复绑定事件处理器:
const boundElements = new WeakSet();
function bindClickOnce(element, handler) {
if (!boundElements.has(element)) {
element.addEventListener('click', handler);
boundElements.add(element);
}
}
// 使用
const btn = document.getElementById('my-btn');
bindClickOnce(btn, () => console.log('clicked'));
bindClickOnce(btn, () => console.log('clicked')); // 第二次无效
这里的关键是:即使 btn 后续从 DOM 中移除,boundElements 不会阻止它被回收;也没有污染 btn 自身属性;不需要手动清理 boundElements。
容易忽略的一点是:WeakSet 实例本身如果长期持有(比如挂到全局或模块顶层),它不会泄漏内存,但它所引用的对象一旦脱离其他引用链,就立刻不可见——你无法预测何时被回收,所以别依赖“某对象还在 WeakSet 里”做业务判断。











