找回密码
 立即注册
首页 业界区 业界 WeakMap 应用场景与示例

WeakMap 应用场景与示例

方子楠 2025-9-23 19:11:17
WeakMap 是 JavaScript 中一种非常有用的数据结构,它通过弱引用机制来帮助管理内存,防止内存泄漏。简单来说,当你用一个对象作为 WeakMap 的键时,WeakMap 不会阻止这个对象被垃圾回收器回收。一旦这个对象在其他地方没有被引用了,它以及它在 WeakMap 中对应的值就会被自动清理掉。
下面是一个对比 WeakMap 和 Map 主要特性的表格,帮助你快速了解它们的区别:
特性WeakMapMap键类型只接受对象作为键任何类型(对象、原始值)均可作为键引用机制对键是弱引用,不阻止垃圾回收对键是强引用,防止键被垃圾回收可遍历性不可遍历(无 keys(), values(), entries() 方法,无 size 属性)可遍历,有 size 属性内存管理自动清理,不易内存泄漏需手动管理,可能内存泄漏主要使用场景需要与对象生命周期关联的元数据、缓存或私有数据存储需要频繁遍历、查询或维护固定键值对集合的场景WeakMap 主要应用场景与 Demo

WeakMap 的设计特点使得它特别适合用于那些需要将数据与对象关联,但又不想影响这些对象生命周期(即垃圾回收)的场景。
1. 为 DOM 元素存储元数据

当需要为 DOM 元素添加一些附加数据(如状态、事件处理器等)时,如果直接存储在普通对象或 Map 中,即使 DOM 元素从页面上移除,由于 Map 还引用着它,它也不会被垃圾回收,导致内存泄漏。WeakMap 可以自动解决这个问题。
  1. // 创建一个 WeakMap 来存储每个 div 元素的点击次数
  2. const domElementMetadata = new WeakMap();
  3. // 获取一个 div 元素
  4. const myDiv = document.createElement('div');
  5. document.body.appendChild(myDiv);
  6. // 为该 div 元素初始化元数据
  7. domElementMetadata.set(myDiv, { clickCount: 0 });
  8. // 给 div 添加点击事件,更新元数据
  9. myDiv.addEventListener('click', function() {
  10.   const metadata = domElementMetadata.get(myDiv);
  11.   metadata.clickCount++;
  12.   console.log(`该 div 已被点击 ${metadata.clickCount} 次`);
  13. });
  14. // 假设未来某个时刻,myDiv 从 DOM 中被移除,并且没有其他变量引用它
  15. // myDiv.remove(); // 从 DOM 移除
  16. // myDiv = null;   // 移除引用
  17. // 此后,垃圾回收器可以自动回收 myDiv 对象,domElementMetadata 中对应的键值对也会被自动清除
复制代码
2. 存储对象的私有数据

在 JavaScript 中,实现真正的私有成员比较麻烦。WeakMap 可以用于模拟对象的私有属性,这些私有属性会随着对象的销毁而自动消失。
  1. // 使用 WeakMap 来模拟私有属性
  2. const _privateData = new WeakMap();
  3. class MyClass {
  4.   constructor(publicValue) {
  5.     // 将私有数据存储在 WeakMap 中,以当前实例 this 为键
  6.     _privateData.set(this, {
  7.       secret: `This is a secret for ${publicValue}`,
  8.       internalCounter: 0
  9.     });
  10.     this.publicValue = publicValue;
  11.   }
  12.   getSecret() {
  13.     // 只有通过实例方法才能访问到对应的私有数据
  14.     const data = _privateData.get(this);
  15.     data.internalCounter++;
  16.     return data.secret;
  17.   }
  18.   getCallCount() {
  19.     return _privateData.get(this).internalCounter;
  20.   }
  21. }
  22. // 使用类
  23. const instance1 = new MyClass('Instance One');
  24. console.log(instance1.getSecret()); // "This is a secret for Instance One"
  25. console.log(instance1.getCallCount()); // 1
  26. const instance2 = new MyClass('Instance Two');
  27. console.log(instance2.getSecret()); // "This is a secret for Instance Two"
  28. console.log(instance2.getCallCount()); // 1
  29. // 当 instance1 被置为 null,它就可以被垃圾回收,_privateData 中对应的私有数据也会被自动清理
  30. // instance1 = null;
复制代码
3. 缓存计算结果

当需要根据特定对象缓存耗时的计算结果,并且希望缓存的生命周期与该对象保持一致时,WeakMap 是很好的选择。
  1. // 使用 WeakMap 缓存与对象相关的昂贵计算结果
  2. const computationCache = new WeakMap();
  3. function intensiveComputation(obj) {
  4.   // 如果缓存中存在该对象的结果,则直接返回
  5.   if (computationCache.has(obj)) {
  6.     console.log('从缓存中获取结果');
  7.     return computationCache.get(obj);
  8.   }
  9.   // 模拟一个耗时的计算过程
  10.   console.log('执行计算...');
  11.   const result = JSON.stringify(obj); // 假设这是一个昂贵的操作
  12.   // 将计算结果缓存到 WeakMap 中,以输入对象为键
  13.   computationCache.set(obj, result);
  14.   return result;
  15. }
  16. // 使用缓存函数
  17. const inputObj1 = { data: "test1" };
  18. const result1 = intensiveComputation(inputObj1); // 输出 "执行计算..."
  19. const result1Cached = intensiveComputation(inputObj1); // 输出 "从缓存中获取结果"
  20. const inputObj2 = { data: "test2" };
  21. const result2 = intensiveComputation(inputObj2); // 输出 "执行计算..."
  22. // 当 inputObj1 不再被需要,并被置为 null 时
  23. // inputObj1 = null;
  24. // 垃圾回收后,computationCache 中对应的缓存项也会被自动清除
复制代码
⚠️ 使用 WeakMap 的注意点


  • 键必须是对象:WeakMap 的键只能是对象(包括数组、函数等),不能是原始值(如字符串、数字、Symbol、null、undefined)。尝试使用原始值作为键会抛出 TypeError。
  • 不可遍历:由于弱引用的特性,WeakMap 没有 size 属性,也不能遍历其键或值(例如,没有 keys(), values(), entries() 方法,也不能使用 forEach)。你只能通过 get(key), set(key, value), has(key), 和 delete(key) 来操作单个键值对。
  • 不支持 clear() 方法:WeakMap 没有清空所有键值对的方法。
  • 垃圾回收时机不确定:虽然 WeakMap 中的键值对会在键对象被垃圾回收后自动消失,但垃圾回收的具体发生时机是由 JavaScript 引擎决定的,你无法立即感知到。
何时选择 WeakMap  vs. Map


  • 选择 WeakMap 的情况:
    你需要将数据(元数据、缓存、私有属性)与对象关联起来,并且希望这些数据的生命周期跟随该对象自动管理避免内存泄漏。你也不需要遍历这些数据或知道其数量。
  • 选择 Map 的情况:
    你的键可以是任何类型(包括原始值)。你需要遍历键值对、需要知道数量(size)、或者需要长期稳定地维护一组键值对集合,而不希望键被自动垃圾回收。
希望这些解释和示例能帮助你更好地理解和使用 WeakMap。
关注一下呗

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册