Set和WeakSet设计目标不同,不能配合管理同一对象集合:Set持强引用、可遍历查询;WeakSet持弱引用、不可遍历、不阻止GC。

Set 和 WeakSet 都用于存储对象,但设计目标和行为完全不同,它们不能“配合使用”来共同管理同一组对象集合——不是功能互补的关系,而是适用场景截然不同的两种工具。
简单说:
- Set 存的是“强引用”对象,能遍历、能查 size、能长期持有对象不被回收;
- WeakSet 存的是“弱引用”对象,无法遍历、没有 size、对象一旦失去其他引用就会自动消失。
把它们混用在同一逻辑里,不仅没协同增益,反而容易引发内存误判或不可预期的丢失。
Set 适合:需要主动管理、稳定存在、可查询的对象集合
比如:
立即学习“Java免费学习笔记(深入)”;
- 记录当前页面中所有已高亮的 DOM 元素(需手动 add/remove)
- 维护一组唯一配置对象,供后续校验或批量操作
- 实现对象级别的去重(如避免重复注册监听器)
const activeModals = new Set();
function openModal(el) {
if (!activeModals.has(el)) {
activeModals.add(el);
el.show();
}
}
function closeModal(el) {
activeModals.delete(el);
el.hide();
}✅ 可随时 has() 判断、size 查数量、forEach 遍历处理
❌ 若不手动 delete,这些 DOM 元素会被 Set 持有,阻止垃圾回收
WeakSet 适合:临时标记、生命周期绑定、不干预内存的对象附注
典型用途:
- 标记某个对象是否已被初始化(避免重复初始化)
- 检测循环引用(如深克隆/序列化时)
- 给第三方对象打“私有标签”,又不想影响其生命周期
const initialized = new WeakSet();
function initUser(obj) {
if (initialized.has(obj)) return;
// 执行初始化逻辑
obj._inited = true;
initialized.add(obj); // 弱引用,不影响 obj 回收
}✅ 对象被销毁后,WeakSet 自动清理对应条目,无内存泄漏风险
❌ 无法知道当前有几个对象被标记、不能遍历、不能做批量操作
为什么不能“配合存储同一集合”?
假设你写:
const set = new Set();
const weakSet = new WeakSet();
const obj = { id: 1 };
set.add(obj);
weakSet.add(obj);表面看两个结构都存了 obj,但:
-
set会让obj永远无法被 GC(只要 set 存在) -
weakSet此时形同虚设——它不阻止回收,但set已经强引用了,所以 weakSet 里那条记录也一直存在,毫无意义 - 若之后
set.delete(obj),obj才可能被回收,此时 weakSet 中的条目才真正起作用——但这不是“配合”,只是先后发生、互不感知的独立行为
换句话说:WeakSet 不是 Set 的“轻量版”或“备用缓存”,它是为不同问题设计的独立原语。
真实协作场景(极少,且非“共管同一集合”)
唯一接近“配合”的用法,是用 WeakSet 做元数据容器,Set 做主数据容器,但二者职责分明、不交叉:
// 主集合:管理活跃实例(强引用)
const instances = new Set();
// 元数据:标记哪些实例已做过某预处理(弱引用,防泄漏)
const processed = new WeakSet();
function createInstance(config) {
const inst = new MyClass(config);
instances.add(inst);
if (!processed.has(inst)) {
doExpensiveSetup(inst);
processed.add(inst);
}
return inst;
}
// 销毁时只需清理主集合
function destroyInstance(inst) {
instances.delete(inst); // inst 若无其他引用,即可回收;processed 条目自动消失
}这里 WeakSet 只是辅助 instances 的内部状态标记,不参与集合管理本身。
不复杂但容易忽略。


















