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 相关文章推荐
下载网站打开页面后间隔多少时间才显示下载链接地址的代码
Apr 25 Javascript
基于Jquery的温度计动画效果
Jun 18 Javascript
jquery修改属性值实例代码(设置属性值)
Jan 06 Javascript
javascript怎么禁用浏览器后退按钮
Mar 27 Javascript
JavaScript实现更改网页背景与字体颜色的方法
Feb 02 Javascript
jQuery时间轴插件使用详解
Jul 16 Javascript
仿Angular Bootstrap TimePicker创建分钟数-秒数的输入控件
Jul 01 Javascript
详解使用angular-cli发布i18n多国语言Angular应用
May 20 Javascript
解决vue中post方式提交数据后台无法接收的问题
Aug 11 Javascript
JS复杂判断的更优雅写法代码详解
Nov 07 Javascript
小程序实现投票进度条
Nov 20 Javascript
详解react组件通讯方式(多种)
May 06 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的博客ping服务代码
2012/02/04 PHP
PHP包含文件函数include、include_once、require、require_once区别总结
2014/04/05 PHP
thinkphp缓存技术详解
2014/12/09 PHP
php实现统计目录文件大小的函数
2015/12/25 PHP
JavaScript DOM 学习第二章 编辑文本
2010/02/19 Javascript
JQuery浮动DIV提示信息并自动隐藏的代码
2010/08/29 Javascript
在Ubuntu上安装最新版本的Node.js
2014/07/14 Javascript
jquery JSON的解析方式示例介绍
2014/07/27 Javascript
Javascript window对象详解
2014/11/12 Javascript
用JavaScript判断CSS浏览器类型前缀的两种方法
2015/10/08 Javascript
如何实现JavaScript动态加载CSS和JS文件
2020/12/28 Javascript
javascript基础语法学习笔记
2016/01/04 Javascript
js实现简单排列组合的方法
2016/01/27 Javascript
JavaScript知识点总结(四)之逻辑OR运算符详解
2016/05/31 Javascript
js 原生判断内容区域是否滚动到底部的实例代码
2017/11/15 Javascript
JS 中document.write()的用法和清空的原因浅析
2017/12/04 Javascript
JS实现的倒计时恢复按钮点击功能【可用于协议阅读倒计时】
2018/04/19 Javascript
解决修复npm安装全局模块权限的问题
2018/05/17 Javascript
Vue插件打包与发布的方法示例
2018/08/20 Javascript
解决vue跨域axios异步通信问题
2019/04/17 Javascript
js实现轮播图效果 纯js实现图片自动切换
2020/08/09 Javascript
Vue项目如何引入bootstrap、elementUI、echarts
2020/11/26 Vue.js
[44:37]完美世界DOTA2联赛PWL S3 Forest vs access 第一场 12.11
2020/12/13 DOTA
解决python2.7 查询mysql时出现中文乱码
2016/10/09 Python
Python表示矩阵的方法分析
2017/05/26 Python
python探索之BaseHTTPServer-实现Web服务器介绍
2017/10/28 Python
Python实现的基数排序算法原理与用法实例分析
2017/11/23 Python
使用python的pandas为你的股票绘制趋势图
2019/06/26 Python
Django路由层URLconf作用及原理解析
2020/09/24 Python
Python实现粒子群算法的示例
2021/02/14 Python
一级方程式赛车官方网上商店:F1 Store(支持中文)
2018/01/12 全球购物
英国网上购买肉类网站:Great British Meat
2018/10/17 全球购物
int *p=NULL和*p= NULL有什么区别
2014/10/23 面试题
北京RT科技有限公司.net工程师面试题
2013/02/15 面试题
2015年小学生新年寄语
2014/12/08 职场文书
解决Pytorch修改预训练模型时遇到key不匹配的情况
2021/06/05 Python