WeakMap
在 JavaScript 中的经典应用主要集中在 私有数据存储、DOM 元素关联数据 和 缓存管理 等场景。以下是几个经典示例:
1.私有属性存储(模拟私有成员)
在 ES6 之前,JavaScript 没有真正的私有属性(#privateField
),使用 WeakMap
可以模拟类的私有成员,避免被外部访问。
示例:为类实例存储私有数据
const privateData = new WeakMap();
class Person {
constructor(name, age) {
// 将私有数据存储在 WeakMap 中,以实例为键
privateData.set(this, { name, age });
}
getName() {
return privateData.get(this).name;
}
getAge() {
return privateData.get(this).age;
}
}
const person = new Person("Alice", 30);
console.log(person.getName()); // "Alice"
console.log(person.name); // undefined(无法直接访问私有属性)
优势:
- 数据真正私有,外部无法通过
person.name
访问。 - 当
person
实例被垃圾回收时,WeakMap
中的关联数据自动清除。
2.DOM 元素关联额外数据
当需要为 DOM 元素存储临时状态或元数据时,WeakMap
可以避免直接修改 DOM 对象属性。
示例:跟踪按钮点击次数
const buttonClicks = new WeakMap();
const button = document.querySelector("#myButton");
// 初始化点击次数
buttonClicks.set(button, { count: 0 });
button.addEventListener("click", () => {
const data = buttonClicks.get(button);
data.count++;
console.log(`Clicked ${data.count} times`);
});
优势:
- 不污染 DOM 元素(如
button._clickCount
这样直接赋值可能引发冲突)。 - 如果按钮从 DOM 中移除,关联数据会自动被垃圾回收。
3.缓存计算结果(Memoization)
WeakMap
适合缓存与对象关联的计算结果,当对象销毁时缓存自动失效。
示例:缓存 DOM 元素的计算样式
const styleCache = new WeakMap();
function getComputedStyle(element) {
if (styleCache.has(element)) {
return styleCache.get(element); // 返回缓存结果
}
const computedStyle = window.getComputedStyle(element);
styleCache.set(element, computedStyle); // 缓存结果
return computedStyle;
}
const div = document.querySelector("div");
console.log(getComputedStyle(div)); // 首次计算并缓存
console.log(getComputedStyle(div)); // 直接返回缓存
优势:
- 避免重复计算,提升性能。
- 当
div
被移除时,缓存自动清除,节省内存。
4.管理对象监听器
为对象动态添加/移除事件监听器时,可以用 WeakMap
跟踪监听器,避免手动清理。
示例:为对象关联事件处理器
const eventListeners = new WeakMap();
function addListener(obj, handler) {
const listeners = eventListeners.get(obj) || [];
listeners.push(handler);
eventListeners.set(obj, listeners);
}
function triggerEvent(obj) {
const listeners = eventListeners.get(obj) || [];
listeners.forEach((handler) => handler());
}
const user = { id: 1 };
addListener(user, () => console.log("Event triggered!"));
triggerEvent(user); // 输出 "Event triggered!"
优势:
- 当
user
对象不再使用时,关联的监听器会自动被垃圾回收。
5.避免内存泄漏的缓存
普通 Map
缓存对象时会导致内存泄漏(即使对象已无用,但 Map
仍保留引用),而 WeakMap
不会。
示例:缓存图像对象
const imageCache = new WeakMap();
function loadImage(url) {
const img = new Image();
img.src = url;
imageCache.set(img, { loaded: false });
img.onload = () => {
imageCache.set(img, { loaded: true });
};
return img;
}
const myImage = loadImage("example.jpg");
// 当 myImage 不再被引用时,imageCache 中的条目自动清除。
总结:WeakMap
的经典应用场景
场景 | 关键优势 |
---|---|
私有属性存储 | 数据真正私有,随实例销毁自动清理 |
DOM 元素关联数据 | 不污染 DOM,自动垃圾回收 |
缓存计算结果 | 对象销毁时缓存自动失效 |
管理监听器 | 避免手动移除监听器,防止内存泄漏 |
临时元数据存储 | 安全地关联对象与临时数据 |
何时选择 WeakMap
?
- 需要将数据与对象生命周期绑定。
- 不希望数据暴露或污染原始对象。
- 键必须是对象(不能是原始值如字符串、数字)。
注意:WeakMap
不可遍历(没有 keys()
/values()
方法),因此仅适用于键对象已知的场景。