javascript的WeakMap和WeakSet有何特性?【教程】

8次阅读

Weakmap和WeakSet的核心特性是键或值的弱引用不阻止垃圾回收。WeakMap只接受对象作键、不可遍历;WeakSet只存对象、无size属性且不可遍历;二者均不触发GC,也不能用于缓存或序列化。

javascript的WeakMap和WeakSet有何特性?【教程】

WeakMap 和 WeakSet 的核心特性不是“弱引用”本身,而是它们对键(WeakMap)或值(WeakSet)的引用不阻止垃圾回收——这意味着你无法通过它们“持有”对象而不影响其生命周期。

WeakMap 只接受对象作为键,且无法遍历

WeakMap 的 key 必须是对象(包括函数、数组、普通对象),传入原始值(如 "str"42true)会直接报错:TypeError: Invalid value used as weak map key。它没有 keys()values()entries()forEach 方法,也无法用 for...of 遍历。

常见用途是给第三方对象“附加私有元数据”,又不干扰其回收:

const privateData = new WeakMap();  class Button {   constructor(el) {     this.el = el;     privateData.set(el, { clickedCount: 0, lastClickTime: Date.now() });   }    click() {     const data = privateData.get(this.el);     data.clickedCount++;   } }

如果 eldom 移除且无其他引用,它连同 privateData 中对应的条目会被 GC 回收。

立即学习Java免费学习笔记(深入)”;

WeakSet 只存对象,且不能查 size 或遍历

WeakSet 的成员只能是对象,同样拒绝原始值;它没有 size 属性,也没有 has() 以外的查询方法(注意:has() 是可用的);也不能遍历。它的典型场景是标记“已处理对象”,避免重复操作:

  • 在深克隆中防止循环引用:seen 用 WeakSet 存已访问过的源对象
  • 事件监听器中记录“已绑定过 cleanup 的对象”,避免多次调用 cleanup

示例:

const processed = new WeakSet();  function process(obj) {   if (processed.has(obj)) return;   // ...实际处理逻辑   processed.add(obj); }

只要 obj 外部引用消失,processed 不会阻止它被回收。

WeakMap/WeakSet 不触发 GC,也不保证立即清理

它们不控制垃圾回收时机,只“不阻碍”回收。即使对象已无强引用,GC 也可能尚未运行,此时 get()has() 仍可能返回值。不能依赖它们做“资源释放通知”或“生命周期钩子”。

  • 不要用 WeakMap.get(key) === undefined 判断对象是否已被回收——它可能只是没设过,或刚设完还没 GC
  • 不能用 WeakMap 实现缓存(比如 LRU),因为无法枚举、无法控制淘汰逻辑
  • node.js 中可通过 global.gc()(需启动时加 --expose-gc)辅助测试,但生产环境禁用

与 Map/Set 的关键差异总结

WeakMap/WeakSet 的限制全是为支持弱引用让路:

  • Map 键可以是任意类型;WeakMap 键只能是对象
  • SetObject.is() 比较值;WeakSet 同样只靠引用相等,但无法迭代验证
  • Map.prototype.clear() 存在;WeakMap 没有 clear(),也无法清空——设计上就不可逆
  • 序列化(jsON.stringify)对 WeakMap/WeakSet 无意义,它们不会出现在任何序列化结果中

真正需要弱引用语义时才用它们;想存配置、缓存、索引,还是老实用 Map/Set —— 弱引用不是免费午餐,是带约束的取舍。

text=ZqhQzanResources