开发工具【免费下载链接】javascript-state-machineA javascript finite state machine library项目地址https://gitcode.com/gh_mirrors/ja/javascript-state-machine点击查看免费下载本指南围绕 javascript-state-machine 提供的状态机工厂State Machine Factory机制展开核心解决如何用同一套配置批量创建相互独立的状态机实例这一实战问题。你将掌握StateMachine.factory()的两种调用形态传入配置对象 / 传入已有类、工厂实例与单例实例在数据隔离上的关键差异以及StateMachine.apply()向现有对象如 React 组件和现有类注入状态机行为的完整做法。从单例构造到工厂构造为什么要引入工厂文档中的大多数示例只构造一个状态机实例例如经典的物态变化模型var fsm new StateMachine({ init: solid, transitions: [ { name: melt, from: solid, to: liquid }, { name: freeze, from: liquid, to: solid }, { name: vaporize, from: liquid, to: gas }, { name: condense, from: gas, to: liquid } ] });这里fsm是一个独立对象拥有自己的state属性与自动生成的转换方法。但当业务中需要多个实体各自维护独立状态例如多个任务、多个告警对象、多个 UI 组件逐个重复书写同一份配置显然不可取。此时应当使用State Machine Factory它基于同一份配置产出一个 JavaScript 构造函数即一个类再由该构造函数实例化出任意多个相互独立的状态机。使用 StateMachine.factory 创建可复用构造函数StateMachine.factory()接收与new StateMachine(options)完全相同的配置对象返回一个构造函数之后用new即可得到状态机实例var Matter StateMachine.factory({ // -- 工厂在这里被构造 init: solid, transitions: [ { name: melt, from: solid, to: liquid }, { name: freeze, from: liquid, to: solid }, { name: vaporize, from: liquid, to: gas }, { name: condense, from: gas, to: liquid } ] }); var a new Matter(), // -- 实例在这里被构造 b new Matter(), c new Matter(); b.melt(); c.melt(); c.vaporize(); a.state; // solid b.state; // liquid c.state; // gas关键观察点a、b、c都从solid出发但彼此状态完全独立b只执行了melt()停在liquidc又执行了vaporize()推进到gas而从未调用任何转换的a仍停留在solid。工厂返回的是真正的构造函数可用new实例化也可继续在原型上挂载业务方法见下文Person示例。实例的独立性来自哪里从源码实现看factory()的核心逻辑在 src/app.js它先根据配置构造一个共享的Config对象再通过build(cstor.prototype, config)把公共方法挂到原型上最后返回构造函数cstorfunction factory() { var cstor, options; if (typeof arguments[0] function) { cstor arguments[0]; options arguments[1] || {}; } else { cstor function() { this._fsm.apply(this, arguments) }; options arguments[0] || {}; } var config new Config(options, StateMachine); build(cstor.prototype, config); cstor.prototype._fsm.config config; // convenience access to shared config without needing an instance return cstor; }而实例的私有状态state则在每次new时由 src/jsm.js 创建独立的JSM对象来承载function JSM(context, config) { this.context context; this.config config; this.state config.init.from; this.observers [context]; }因此可以得出准确结论每个实例是唯一对象各自管理自己的state属性而is、can、cannot、transitions、allStates、allTransitions等方法通过 JavaScript 正常的原型机制被所有实例共享。这一点在 test/construction.js 中也有直接断言t.is(fsm1.allStates, MyClass.prototype.allStates)验证了方法共享而fsm1.state/fsm2.state/fsm3.state分别等于A/B/C验证了状态隔离。工厂实例的数据与方法一个必须注意的共享陷阱特别注意关于数据和状态机工厂需要看 Data and State Machine Factories 中的特殊处理说明。当从工厂构造多个实例时如果配置中data是一个普通对象那么该对象会被所有实例共享——这几乎可以肯定不是你想要的。看一个反面演示var FSM StateMachine.factory({ init: A, transitions: [ { name: step, from: A, to: B } ], data: { color: red } // -- 危险所有实例共享同一个 color 对象 });要保证每个实例获得独立的数据应把data写成函数工厂方法让它在每个实例构造时被调用一次并返回新对象构造实例时传入的参数会原样透传给该函数var FSM StateMachine.factory({ init: A, transitions: [ { name: step, from: A, to: B } ], data: function(color) { // -- 使用方法每个实例各自调用 return { color: color } }, methods: { describe: function() { console.log(I am this.color); } } }); var a new FSM(red), b new FSM(blue); a.state; // A b.state; // A a.color; // red b.color; // blue a.describe(); // I am red b.describe(); // I am blue注意new FSM(red)/new FSM(blue)中的构造参数red、blue会直接透传给data函数。这条规则的底层实现在 src/config.jsConfig.configureData会把对象形式的data包装成function() { return data; }每次返回同一引用把函数形式的data原样保留随后在 src/jsm.js 的init中通过this.config.data.apply(this.context, args)执行并mixin到实例上下文上。这与 test/construction.js 中每个实例携带独立value并互不影响的用例完全对应。methods中的方法同样被挂到原型上共享但方法内部的this始终指向调用它的那个实例因此访问this.state、this.color时得到的都是该实例自己的值。工厂与初始化init 转换对每个实例分别触发工厂构造的类其init转换隐式或显式会在每个实例被new出来时各自触发一次。例如var FSM StateMachine.factory({ init: A, transitions: [ { name: step, from: A, to: B }, { name: step, from: B, to: C } ] }); var fsm1 new FSM(), // init() transition fires from none to A for fsm1 fsm2 new FSM(); // init() transition fires from none to A for fsm2也就是说每个实例都会独立经历从none到初始状态的初始化流程并触发相应的生命周期事件。更多初始化细节显式init转换、隐式init转换参见 Initialization Options。向已有对象应用状态机行为StateMachine.apply有时你并不想创建新类而是希望给已经存在的对象例如一个 React 组件实例、一个普通配置对象直接赋予状态机能力。此时使用StateMachine.applyvar component { /* ... */ }; StateMachine.apply(component, { init: A, transitions: [ { name: step, from: A, to: B } ] });调用后component会获得state属性、step()等转换方法以及is/can等内省方法并且立即进入初始状态A。注意不要使用会与对象已有属性冲突的状态名或转换名例如对象上已有state或step字段会被状态机覆盖或引发异常。测试 test/basics.js 给出了完整验证对一个{ name: alarm }对象apply后原属性name保留state变为green且warn/panic/calm/clear转换都能正确推进状态。从 src/app.js 的实现看apply与factory走的是同一套build流程区别仅在于apply直接作用于目标对象并立即调用instance._fsm()完成初始化。另外StateMachine.apply只接受普通对象如果目标是函数或数组会抛出Error(StateMachine can only be applied to objects)这一点由 src/app.js 的build前置校验保证并在 test/errors.js 中对function、[]、42等非法目标逐一验证。向已有类应用工厂行为StateMachine.factory(Class, config)如果对象是一个类构造函数同样可以把状态机工厂行为注入其中但此时你必须自己负责初始化在类的构造函数里显式调用this._fsm()。function Person(name) { this.name name; this._fsm(); // -- 重要必须手动调用以完成该实例的状态机初始化 } Person.prototype { speak: function() { console.log(my name is this.name and I am this.state); } } StateMachine.factory(Person, { init: idle, transitions: [ { name: sleep, from: idle, to: sleeping }, { name: wake, from: sleeping, to: idle } ] }); var amy new Person(amy), bob new Person(bob); bob.sleep(); amy.state; // idle bob.state; // sleeping amy.speak(); // my name is amy and I am idle bob.speak(); // my name is bob and I am sleeping要点拆解StateMachine.factory的第一种重载形态就是传入已有构造函数 配置对象对应 src/app.js 中typeof arguments[0] function的分支。它会把状态机方法构建到Person.prototype上因此amy.speak()、bob.sleep()都可用。this._fsm()是关键它创建该实例私有的JSM内核并执行init见 src/app.js。amy、bob各自调用所以amy停在idle而bob进入sleeping互不干扰。你自定义的原型方法speak与状态机方法共存于同一原型链this.state在方法中可直接读取当前实例状态。test/basics.js 中的Alarm类测试与此模式一致a、b两个实例分别执行warn/panic/calm/clear时彼此的state完全隔离验证了同一类上多个实例状态独立这一核心保证。方法命名转换名如何映射为方法名工厂或 apply 会把每个转换自动映射为实例上的方法映射规则在 src/util/camelize.js按-/_分割单词首单词小写后续单词首字母大写并拼接例如on-before变为onBefore。而 src/app.js 中通过target[camelize(transition)] function() { return this._fsm.fire(transition, ...) }动态生成方法——因此转换名最好采用下划线或连字符风格如step-1生成的方法名即为驼峰形式step1若转换名已是全小写单词方法名保持原样。仓库中的完整实践matter 示例工厂模式的完整可运行示例位于 examples/matter.js它用StateMachine.factory构造Matter类物态solid/liquid/gas并在类上挂载visualize方法输出可视化状态图。仓库已提供对应的状态图渲染结果 examples/matter.png该图对应的.dot源文件与 SVG 版本分别是 examples/matter.dot 和 [examples/matter.svg]可作为工厂构造的状态机结构的直观参考。小结三种形态的选择建议场景推荐做法初始化方式只需一个状态机实例new StateMachine(options)构造时自动完成用同一配置批量创建独立实例StateMachine.factory(options)new每个实例构造时自动完成给已有对象注入状态机能力StateMachine.apply(obj, options)调用apply时立即完成给已有类注入状态机能力StateMachine.factory(Class, options)必须在类构造函数中手动调用this._fsm()四个通用提醒其一工厂 对象形式data会导致数据被所有实例共享务必改用data函数其二避免状态名/转换名与目标对象已有属性冲突其三工厂实例的状态各自独立、方法原型共享其四已有类的初始化必须手动调用this._fsm()否则实例不会进入任何状态机状态。赞分享开发工具【免费下载链接】javascript-state-machineA javascript finite state machine library项目地址https://gitcode.com/gh_mirrors/ja/javascript-state-machine点击查看免费下载相关推荐网页监控自动化告别手动刷新用urlwatch智能追踪网页变化网页监控自动化告别手动刷新用urlwatch智能追踪网页变化 你是否曾经为了监控商品价格变化而不断刷新网页或者需要追踪新闻网站的最新动态却总是错过重要更新后端开发工具JavaScript状态机可视化指南使用javascript-state-machine生成状态图JavaScript状态机可视化指南使用javascript state machine生成状态图 状态机是软件开发中一个非常有用的概念它可以帮助我们清晰地开发工具终极指南使用sys-clk优化你的Nintendo Switch游戏性能终极指南使用sys clk优化你的Nintendo Switch游戏性能 在Nintendo Switch的游戏世界中你是否曾遇到过帧率不稳定、加载时间过长系统编程上一篇彻底掌握 Timewarrior 配置系统从文件到命令行的全维度控制下一篇Jester中间件开发指南扩展框架功能的高级方法创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
