javascript与有限状态机详解


Posted in Javascript onMay 08, 2014

简单说,它有三个特征:

* 状态总数(state)是有限的。
* 任一时刻,只处在一种状态之中。
* 某种条件下,会从一种状态转变(transition)到另一种状态。

它对JavaScript的意义在于,很多对象可以写成有限状态机。

举例来说,网页上有一个菜单元素。鼠标悬停的时候,菜单显示;鼠标移开的时候,菜单隐藏。如果使用有限状态机描述,就是这个菜单只有两种状态(显示和隐藏),鼠标会引发状态转变。

代码可以写成下面这样:

var menu = {

// 当前状态


currentState: 'hide',


// 绑定事件


initialize: function() {



var self = this;



self.on("hover", self.transition);


},


// 状态转换


transition: function(event){



switch(this.currentState) {




case "hide":





this.currentState = 'show';





doSomething();





break;




case "show":





this.currentState = 'hide';





doSomething();





break;




default:





console.log('Invalid State!');





break;



}


}

};

可以看到,有限状态机的写法,逻辑清晰,表达力强,有利于封装事件。一个对象的状态越多、发生的事件越多,就越适合采用有限状态机的写法。

另外,JavaScript语言是一种异步操作特别多的语言,常用的解决方法是指定回调函数,但这样会造成代码结构混乱、难以测试和除错等问题。有限状态机提供了更好的办法:把异步操作与对象的状态改变挂钩,当异步操作结束的时候,发生相应的状态改变,由此再触发其他操作。这要比回调函数、事件监听、发布/订阅等解决方案,在逻辑上更合理,更易于降低代码的复杂度。

下面介绍一个有限状态机的函数库Javascript Finite State Machine。这个库非常好懂,可以帮助我们加深理解,而且功能一点都不弱。

该库提供一个全局对象StateMachine,使用该对象的create方法,可以生成有限状态机的实例。

var fsm = StateMachine.create();

生成的时候,需要提供一个参数对象,用来描述实例的性质。比如,交通信号灯(红绿灯)可以这样描述:

var fsm = StateMachine.create({initial: 'green',
events: [
{ name: 'warn',  from: 'green',  to: 'yellow' },
{ name: 'stop', from: 'yellow', to: 'red' },
{ name: 'ready',  from: 'red',    to: 'yellow' },
{ name: 'go', from: 'yellow', to: 'green' }
]
});

交通信号灯的初始状态(initial)为green,events属性是触发状态改变的各种事件,比如warn事件使得green状态变成yellow状态,stop事件使得yellow状态变成red状态等等。

生成实例以后,就可以随时查询当前状态。

* fsm.current :返回当前状态。
* fsm.is(s) :返回一个布尔值,表示状态s是否为当前状态。
* fsm.can(e) :返回一个布尔值,表示事件e是否能在当前状态触发。
* fsm.cannot(e) :返回一个布尔值,表示事件e是否不能在当前状态触发。

Javascript Finite State Machine允许为每个事件指定两个回调函数,以warn事件为例:
* onbeforewarn:在warn事件发生之前触发。
* onafterwarn(可简写成onwarn) :在warn事件发生之后触发。

同时,它也允许为每个状态指定两个回调函数,以green状态为例:

* onleavegreen :在离开green状态时触发。
* onentergreen(可简写成ongreen) :在进入green状态时触发。

假定warn事件使得状态从green变为yellow,上面四类回调函数的发生顺序如下:onbeforewarn → onleavegreen → onenteryellow → onafterwarn。

除了为每个事件和状态单独指定回调函数,还可以为所有的事件和状态指定通用的回调函数。

* onbeforeevent :任一事件发生之前触发。
* onleavestate :离开任一状态时触发。
* onenterstate :进入任一状态时触发。
* onafterevent :任一事件结束后触发。

如果事件的回调函数里面有异步操作(比如与服务器进行Ajax通信),这时我们可能希望等到异步操作结束,再发生状态改变。这就要用到transition方法。

fsm.onwarn = function(){
light.fadeOut('slow', function() {
fsm.transition();
});
return StateMachine.ASYNC;
};

上面代码的回调函数里面,有一个异步操作(light.fadeOut)。如果不希望状态立即改变,就要让回调函数返回一个StateMachine.ASYNC对象,表示状态暂时不改变;等到异步操作结束,再调用transition方法,使得状态发生改变。

Javascript Finite State Machine还允许指定错误处理函数,当发生了当前状态不可能发生的事件时自动触发。

var fsm = StateMachine.create({
// ...
error: function(eventName, from, to, args, errorCode, errorMessage) {
return 'event ' + eventName + ': ' + errorMessage;
},
// ... 
});

比如,当前状态是green,理论上这时只可能发生warn事件。要是这时发生了stop事件,就会触发上面的错误处理函数。

Javascript 相关文章推荐
javascript this用法小结
Dec 19 Javascript
JavaScript 仿关机效果的图片层
Dec 26 Javascript
JavaScript操纵窗口的方法小结
Jun 28 Javascript
JavaScript获取onclick、onchange等事件值的代码
Jul 22 Javascript
JS获取DropDownList的value值与text值的示例代码
Jan 07 Javascript
AngularJS ng-style中使用filter
Sep 21 Javascript
vue2实现移动端上传、预览、压缩图片解决拍照旋转问题
Apr 13 Javascript
深入浅析Vue.js中 computed和methods不同机制
Mar 22 Javascript
JS执行控制之节流模式实例分析
Dec 21 Javascript
浅谈JS中this在各个场景下的指向
Aug 14 Javascript
详解JS函数防抖
Jun 05 Javascript
JS数组及对象遍历方法代码汇总
Jun 16 Javascript
ajax提交表单实现网页无刷新注册示例
May 08 #Javascript
JavaScript怎么判断图片是否加载完成以便获取其尺寸
May 08 #Javascript
js动态删除div元素基本思路及实现代码
May 08 #Javascript
JS的encodeURI和java的URLDecoder.decode使用介绍
May 08 #Javascript
jquery查找tr td 示例模拟
May 08 #Javascript
js冒泡、捕获事件及阻止冒泡方法详细总结
May 08 #Javascript
JavaScript二维数组实现的省市联动菜单
May 08 #Javascript
You might like
用PHP写的MySQL数据库用户认证系统代码
2007/03/22 PHP
zf框架的Filter过滤器使用示例
2014/03/13 PHP
[原创]php使用strpos判断字符串中数字类型子字符串出错的解决方法
2017/04/01 PHP
js版本A*寻路算法
2006/12/22 Javascript
计算世界完全对称日的js代码,粗糙版
2011/11/04 Javascript
探索Emberjs制作一个简单的Todo应用
2012/11/07 Javascript
js confirm()方法的使用方法实例
2013/07/13 Javascript
javascript HTML5 canvas实现打砖块游戏
2020/06/18 Javascript
javascript数组常用方法汇总
2016/09/10 Javascript
详解Javascript函数声明与递归调用
2016/10/22 Javascript
JS判断输入的字符串是否是数字的方法(正则表达式)
2016/11/29 Javascript
Jquery中.bind()、.live()、.delegate()和.on()之间的区别详解
2017/08/01 jQuery
Nodejs中的require函数的具体使用方法
2019/04/02 NodeJs
JS代码屏蔽F12,右键,粘贴,复制,剪切,选中,操作实例
2019/09/17 Javascript
使用Python脚本将文字转换为图片的实例分享
2015/08/29 Python
python3使用urllib模块制作网络爬虫
2016/04/08 Python
关于Python正则表达式 findall函数问题详解
2018/03/22 Python
python3 http提交json参数并获取返回值的方法
2018/12/19 Python
在Python 不同级目录之间模块的调用方法
2019/01/19 Python
利用python numpy+matplotlib绘制股票k线图的方法
2019/06/26 Python
详细介绍Python进度条tqdm的使用
2019/07/31 Python
Python2和Python3中@abstractmethod使用方法
2020/02/04 Python
python 实现在无序数组中找到中位数方法
2020/03/03 Python
Python进程间通信multiprocess代码实例
2020/03/18 Python
python自动提取文本中的时间(包含中文日期)
2020/08/31 Python
Python爬取股票信息,并可视化数据的示例
2020/09/26 Python
css3一款3D字体带阴影效果的实现步骤
2013/03/20 HTML / CSS
世界上最大的隐形眼镜商店:1-800 Contacts
2018/11/03 全球购物
美国在线健康和美容市场:Pharmapacks
2018/12/05 全球购物
英国屋顶用品和材料超市:Roofing Supplies UK
2019/08/24 全球购物
vue+django实现下载文件的示例
2021/03/24 Vue.js
会计实习生自我鉴定
2013/12/12 职场文书
医学生求职自荐书
2014/06/12 职场文书
企业安全生产目标责任书
2014/07/23 职场文书
党的群众路线教育实践活动整改方案
2014/10/28 职场文书
2019大学生预备党员转正思想汇报
2019/06/21 职场文书