使用场景
实际使用后,我觉得可以从键值对的操作和垃圾回收过程考虑。
首先,如果是涉及遍历操作,自然是选择Map
,weakMap
不支持这些特性。
其次,如果是涉及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 中的项会被自动清除
区别
- 键的类型:
- Map:可以使用任何类型的值作为键,包括对象、函数和基本类型。
- WeakMap:只能使用对象作为键,不能使用基本类型。
- 垃圾回收:
- Map:对键值对的引用是强引用。即使没有其他引用,Map 中的对象仍然会被保留在内存中。
- WeakMap:对键的引用是弱引用。如果没有其他引用指向键对象,垃圾回收器会自动回收这个键及其对应的值。
- 遍历:
- Map:可以通过
forEach
或其他迭代方法遍历。 - WeakMap:不支持遍历,因为它的键是弱引用,无法保证其存在性。
- Map:可以通过
其它
在《JavaScript高级程序设计》中,还介绍了weakMap还可以作为实现私有变量的一种新方式(听闻过,没用过)