Map与WeakMap的使用

Thursday, November 7, 2024

使用场景

Map与WeakMap的区别

实际使用后,我觉得可以从键值对的操作和垃圾回收过程考虑。

首先,如果是涉及遍历操作,自然是选择MapweakMap不支持这些特性。

其次,如果是涉及DOM节点保存相关的操作,可以考虑使用weakMap,因为weakMap属于弱引用,当节点被垃圾回收时,关联的 WeakMap 键值对也会自动被清除,减少心智负担。

强引用和弱引用

const map = new Map();
const obj = {};
map.set(obj, 'value');

// obj 被 map 强引用,垃圾回收器不会回收它
// 简单点理解就是,在强引用关系下,引用计数会+1,所以不会被回收机制回收
const weakMap = new WeakMap();
const obj = {};
weakMap.set(obj, 'value');

// 当 obj 不再被其他引用指向时,垃圾回收器可以回收它
// 弱引用关系下,引用计数+0,所以当没有其它引用指向时,obj就会被回收

保存DOM节点数据

// 使用 Map
const map = new Map();
const button1 = document.querySelector('#button1');
map.set(button1, { disabled: true });

// 此时,即使从 DOM 树中删除了 button1,Map 仍然对其保持强引用,因此 button1 的内存不会被释放。
// 可以重新将 button1 插入回 DOM 树中,内存仍然存在。

// 使用 WeakMap
const weakMap = new WeakMap();
const button2 = document.querySelector('#button2');
weakMap.set(button2, { disabled: true });

// 如果 button2 从 DOM 树中删除,WeakMap 没有持有对 button2 的强引用。
// 如果没有其他强引用指向 button2,它的内存会被释放。

那实际应用场景可以是哪呢,我的用法是:

在计算元素是否达到有效曝光时间时,可以使用 WeakMap DOM 元素作为计时器的键。

  • 自动管理内存:当元素从 DOM 中移除时,WeakMap 会自动解除对该元素的引用,避免内存泄漏。
  • 简化代码:无需手动销毁计时器,减少了额外的清理工作。
// 创建 IntersectionObserver 实例
const observer = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
      if (entry.isIntersecting) {
          console.log('元素进入视口:', entry.target);
          // 可以在这里执行懒加载或其他操作
      } else {
          console.log('元素离开视口:', entry.target);
      }
  });
}, {
  root: null, // 视口为浏览器窗口
  rootMargin: '0px', // 根元素的边距
  threshold: 0.1 // 交叉比例阈值
});

// 观察目标元素
const targetElement = document.querySelector('.target');
observer.observe(targetElement);

// web端由于需要先获取dom实例进行绑定,因此,我们可以利用 WeakMap 的弱引用特性,将 DOM 实例作为键存储相关的定时器,这样在页面销毁时,与之绑定的定时器会自动被清理,无需手动管理。

const timerMap = new WeakMap();

// 设置定时器并存储引用
timerMap.set(element, setTimeout(() => {
// 处理逻辑
}, 1000));

// 当元素被销毁时,WeakMap 中的项会被自动清除

区别

  1. 键的类型
    • Map:可以使用任何类型的值作为键,包括对象、函数和基本类型。
    • WeakMap:只能使用对象作为键,不能使用基本类型。
  2. 垃圾回收
    • Map:对键值对的引用是强引用。即使没有其他引用,Map 中的对象仍然会被保留在内存中。
    • WeakMap:对键的引用是弱引用。如果没有其他引用指向键对象,垃圾回收器会自动回收这个键及其对应的值。
  3. 遍历
    • Map:可以通过 forEach 或其他迭代方法遍历。
    • WeakMap:不支持遍历,因为它的键是弱引用,无法保证其存在性。

其它

在《JavaScript高级程序设计》中,还介绍了weakMap还可以作为实现私有变量的一种新方式(听闻过,没用过)

JavaScript-使用WeakMap创建对象的私有属性