WeakSet 只能存储对象且持有弱引用,适合标记对象的布尔状态(如“已处理”),不阻塞垃圾回收;无法存储原始值,也不能遍历或获取大小。

WeakSet 本身不能直接标记原始值(如字符串、数字、布尔值),因为 WeakSet 只接受对象作为元素,且对对象的引用是弱引用。所以,“用 WeakSet 标记原始对象”这个说法存在概念混淆——原始值不是对象,无法被 WeakSet 存储,更谈不上“垃圾回收友好”。真正可行的是:**用 WeakSet 标记那些本就存在的、可能被频繁创建又丢弃的对象实例,从而实现私有状态关联,且不阻碍其被 GC 回收**。
为什么 WeakSet 适合做“不阻塞 GC”的私有标记
WeakSet 的核心特性是:它持有的对象引用是弱引用,不会阻止垃圾回收器释放该对象。只要对象在其他地方没有任何强引用,即使它还在 WeakSet 中,也会被自动移除(WeakSet 内部条目随之消失)。这使得 WeakSet 成为关联“临时性私有元数据”的理想容器——比如标记某个对象是否已初始化、是否启用调试、是否参与某次计算流程等。
对比 Map 或普通 Set:它们持有强引用,若把目标对象存进去却忘了清理,就会造成内存泄漏。
典型用法:给类实例打“已处理”标记
假设你有一个工具函数,需对传入的对象做一次性的副作用操作(如绑定事件、打日志、触发初始化),但要避免重复执行。你可以用 WeakSet 记录哪些对象已被处理过:
const processed = new WeakSet();
<p>function doOnce(obj) {
if (processed.has(obj)) return;
// 执行一次性逻辑
console.log('Processing:', obj);
processed.add(obj);
}</p><p>// 使用示例
const user = { id: 123 };
doOnce(user); // ✅ 输出
doOnce(user); // ❌ 不输出(已标记)
// user 离开作用域后,processed 中对应条目自动失效,无内存泄漏
进阶技巧:模拟“私有字段”行为(ES2022 # 之前)
在私有字段(#field)普及前,WeakMap 常用于存储私有数据;WeakSet 则适用于只需布尔态标记的场景(如“是否冻结”、“是否已校验”、“是否处于 pending 状态”):
- WeakSet 占用更少内存(只存引用,不存值)
- 适合做“开关型”状态,语义清晰(有 / 无,而非 值是什么)
- 与类配合时,可放在类外部声明,避免污染实例属性
注意事项与边界
WeakSet 不能遍历、不能获取大小(size 为 undefined)、不能清空(没有 clear() 方法),也不能通过对象反查 WeakSet 是否包含它(只能用 has() 查询)。因此它只适合单向判断场景。
另外,WeakSet 无法用于原始值——若你看到类似 “标记字符串” 的需求,实际应转换思路:要么包装成对象(如 { value: 'abc' }),要么改用其它机制(如 Symbol 属性 + 显式清理,或 Map + 手动管理生命周期)。

















