weakmap 可用于状态机中为对象关联私有状态且不阻止垃圾回收:其键为弱引用对象,值为对应状态,对象销毁时条目自动清理,兼具内存安全与封装性。

WeakMap 可以用来在状态机中为对象关联私有状态,而不会阻止对象被垃圾回收。核心思路是:用 WeakMap 存储“对象 → 状态”映射,键是状态机所管理的实例(如 DOM 元素、自定义对象),值是该实例当前的状态(如字符串、数字或对象)。因为 WeakMap 的键是弱引用,当目标对象不再被其他地方引用时,它能自动从 WeakMap 中被清理,避免内存泄漏。
用 WeakMap 管理状态机的私有状态
状态机通常需要为每个被管理的对象维护独立、不可外部访问的状态。WeakMap 天然适合这种场景——它不暴露键集合,无法遍历,且键必须是对象,正好对应“按对象隔离状态”的需求。
- 创建一个全局 WeakMap 实例,作为状态存储容器
- 状态变更时,只操作该 WeakMap,不往目标对象上挂属性(避免污染或被误改)
- 读取状态时,用目标对象作为 key 查 WeakMap;若未查到,可设默认值或触发初始化
一个轻量状态机示例:按钮点击状态流转
假设要实现一个按钮的“空闲 → 加载中 → 成功 → 空闲”循环状态,且状态不能被外部篡改:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
const buttonState = new WeakMap();
class ButtonStateMachine {
static STATES = { IDLE: 'idle', LOADING: 'loading', SUCCESS: 'success' };
static getState(btn) {
return buttonState.get(btn) || ButtonStateMachine.STATES.IDLE;
}
static setState(btn, state) {
if (Object.values(ButtonStateMachine.STATES).includes(state)) {
buttonState.set(btn, state);
btn.setAttribute('data-state', state); // 可选:同步到 DOM
}
}
static transition(btn, event) {
const current = this.getState(btn);
let next = current;
if (current === ButtonStateMachine.STATES.IDLE && event === 'click') {
next = ButtonStateMachine.STATES.LOADING;
} else if (current === ButtonStateMachine.STATES.LOADING && event === 'success') {
next = ButtonStateMachine.STATES.SUCCESS;
} else if (current === ButtonStateMachine.STATES.SUCCESS) {
next = ButtonStateMachine.STATES.IDLE;
}
this.setState(btn, next);
return next;
}
}
// 使用
const btn = document.getElementById('my-btn');
ButtonStateMachine.transition(btn, 'click'); // → loading
ButtonStateMachine.transition(btn, 'success'); // → success
console.log(ButtonStateMachine.getState(btn)); // 'success'
// 卸载 btn 后,WeakMap 中对应条目自动消失
为什么不用普通 Map 或对象属性?
普通 Map 会强引用键,导致即使按钮节点被移除 DOM,只要 Map 还持有它,就无法 GC;直接挂 btn._state 则破坏封装性,属性可被任意代码读写或删除。WeakMap 同时解决了内存安全和封装性两个问题。
- WeakMap 键是弱引用 → 不阻碍垃圾回收
- WeakMap 无 keys()/values() 方法 → 外部无法枚举或清空所有状态
- WeakMap 只接受对象作键 → 天然绑定到实例生命周期
注意事项与边界情况
WeakMap 不是万能的,使用时需注意:
- 不能用原始值(如字符串、数字)作键,必须是对象 —— 如果你只有 id 字符串,得先用 id 查到对应对象再操作
- 无法预知何时清理(取决于 GC),所以不要依赖 WeakMap 的“自动清除”做业务逻辑判断,仅用作内存优化
- 调试困难:开发者工具通常不显示 WeakMap 内容,建议搭配 console.assert 或简单日志辅助验证
- 若需序列化或持久化状态,WeakMap 不适用 —— 它只存在于内存中,且不可遍历
不复杂但容易忽略:WeakMap 提供的不是“隐藏”,而是“绑定生命周期的封装”。它让状态真正属于对象本身,而不是全局变量或闭包,是构建健壮状态机的底层基础设施之一。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










