私有字段#state是状态机安全核心,杜绝外部直接修改;状态变更须经校验方法,配合static #validtransitions表控制合法跃迁,保障状态一致性与防御性。

用 Class 实现状态机时,私有字段是保障状态安全、防止非法跃迁的核心手段——它把当前状态锁死在类内部,外部无法直接赋值或读取,所有转换必须走预设的、带校验的接口。
用 #state 管理唯一可变状态
状态机最核心的状态(如 idle、running、error)应声明为私有字段,杜绝裸改:
- 写法:class Machine { #state = 'idle'; }
- 外部无法执行
machine.#state = 'running',会报SyntaxError - 子类也无法访问该字段,避免继承链中意外覆盖或误用
- 配合 getter 可只读暴露当前状态:
get state() { return this.#state; }
状态变更必须经过受控方法
所有触发状态转移的操作(如 start()、stop()、handleError())都应在类内定义,并内置转换逻辑:
- 检查前置条件:例如只允许从
idle调用start(),否则抛错 - 封装副作用:切换状态时自动记录时间戳、触发回调、清理资源
- 返回布尔值或 Promise 表示是否成功,不暴露内部字段细节
- 示例:
start() { if (this.#state === 'idle') { this.#state = 'running'; this.#log('started'); } }
结合 static # 预置合法状态与转换规则
把状态枚举、允许的转换表等元数据设为静态私有字段,既避免污染实例、又防止外部篡改:
static #validTransitions = { idle: ['running'], running: ['idle', 'error'] };- 转换方法中查表判断:
if (!Machine.#validTransitions[this.#state]?.includes(next)) throw new Error('Invalid transition'); - 这些规则对子类不可见,也不占每个实例的内存,语义清晰且高效
防御性设计:避免反射或运行时绕过
虽然 JavaScript 私有字段目前无法被常规反射 API(如 Object.keys、for...in)访问,但仍需注意:
- 不要在构造函数中接受原始对象并直接解构赋值到私有字段,防止传入恶意代理对象
- 若状态依赖外部输入(如初始化参数),应在构造器中校验其是否属于预设合法值
- 关键状态字段可搭配
readonly(TypeScript)或运行时冻结(Object.freeze)进一步加固











