javascript实现有限状态机
1、状态机描述
简单说,有限状态机是一种模型,模型都用来模拟事物,能够被有限状态机这种模型模拟的事物,一般都有以下特点:
1)可以用状态来描述事物,并且任一时刻,事物总是处于一种状态;
2)事物拥有的状态总数是有限的;
3)通过触发事物的某些行为,可以导致事物从一种状态过渡到另一种状态;
4)事物状态变化是有规则的,A状态可以变换到B,B可以变换到C,A却不一定能变换到C;
5)同一种行为,可以将事物从多种状态变成同种状态,但是不能从同种状态变成多种状态。
2、思考实现
参照已有的实现功能 https://github.com/jakesgordon/javascript-state-machine/
实现思路
1、单个页面会有多个UI,每个UI可拥有独立的一个状态机,因此用工厂模式的思路实现
2、状态切换间应该有多个时机传回调函数:onbefore->onleave->on[Event]->on[state]->onenter[State]->onafter[event]
3、留个外部调用的一些方法接口is(判断当前状态是不是此state);can(判断状态十分能转换);cannot;transitions(能转换的状态);
代码如下
var sm = (function () {
function _StateMachine(config) {
this.state = config.initial||'unknow';
this.events = config.events;
this.callbacks = config.callbacks||{};
this.error = config.error || function(name, from, to, args, error, msg, e) { throw e || msg; };
this._hashEvent = {}
this.init();
}
var __PREFIX = ['onbefore','onleave','onenter','onafter'];
_StateMachine.prototype.SYNC = 'sync';
_StateMachine.prototype.init = function () {
this._initHashEvent();
this._buildEvent();
}
_StateMachine.prototype._initHashEvent = function () {
for (var i = this.events.length - 1; i >= 0; i--) {
var e = this.events[i];
if (!this._hashEvent[e.name]) {
this._hashEvent[e.name] = []
}
this._hashEvent[e.name].push({
from:e.from,
to:e.to,
name:e.name
})
}
}
_StateMachine.prototype._buildEvent = function () {
var self = this;
for(var i in this._hashEvent){
this[i] = (function (i) {
return function (arg) {
var event = this._getEvent(this._hashEvent[i]);
if (JSON.stringify(event) == '{}') {
return false;
}
this.cacheEvent = event; //用于异步调用
var fn = event.name;
this.callbacks['onbefore'+fn]&&this.callbacks['onbefore'+fn](this,event.name,this.state,event.state,arg);
if(this.callbacks['onbefore'+fn]){
var beforeValue = this.callbacks['onbefore'+fn](this,event.name,this.state,event.state,arg);
}
if(beforeValue == false){
return false;
}
if(this.callbacks['onleave'+this.state]){
var leaveValue = this.callbacks['onleave'+this.state](this,event.name,this.state,event.state,arg);
}
if(leaveValue == false){
console.log('not transform')
return false;
}
if(leaveValue == this.SYNC){
return false;
}
this._transition(arg);
}
})(i);
}
}
_StateMachine.prototype._getEvent = function(hash){
var event={};
for (var i = hash.length - 1; i >= 0; i--) {
var formType = Object.prototype.toString.call(hash[i].from);
if(formType == '[object Array]'){
if(hash[i].from.indexOf(this.state)>=0){
event = hash[i];
break;
}
}else{
if(hash[i].from == this.state){
event = hash[i];
break;
}
}
}
return event;
}
_StateMachine.prototype._transition = function (args) {
var event = this.cacheEvent;
var fn = event.name;
this.prevState = this.state;
this.state = event.to;
this.callbacks['on'+event.name]&&this.callbacks['on'+event.name](this,this.prevState,this.state,args);
this.callbacks['on'+this.state]&&this.callbacks['on'+this.state](this,this.prevState,this.state,args);
this.callbacks['onenter'+this.state]&&this.callbacks['onenter'+this.state](this,event.name,this.prevState,event.state,args);
this.callbacks['onafter'+fn]&&this.callbacks['onafter'+fn](this,event.name,this.prevState,event.state,args);
console.log('sm state transform '+ this.prevState+ ' to '+ this.state);
}
_StateMachine.prototype.is = function (state) {
return this.state == state;
}
_StateMachine.prototype.can = function (eventName) {
var event = this._getEvent(this._hashEvent[eventName]);
return JSON.stringify(event) != '{}';
}
_StateMachine.prototype.cannot = function (eventName) {
var event = this._getEvent(this._hashEvent[eventName]);
return JSON.stringify(event) == '{}';
}
//以数组的形式返回实例当前状态下能够被触发的行为列表
_StateMachine.prototype.transitions = function () {
var events = [];
for (var e in this._hashEvent) {
if(this.can(e)){
events.push(e);
}
}
return events;
}
return {
create:function (config) {
return new _StateMachine(config)
}
}
})();
调用
var fsm2 = sm.create({
initial: 'hungry',
events: [
{ name: 'eat', from: 'hungry', to: 'satisfied' },
{ name: 'eat', from: 'satisfied',to: 'full' },
{ name: 'eat', from: 'full',to: 'sick' },
{ name: 'rest', from: ['hungry', 'satisfied', 'full', 'sick'], to: 'hungry' },
]
});
var fsm = sm.create({
initial: 'green',
events: [
{ name: 'warn', from: 'green', to: 'yellow' },
{ name: 'panic', from: 'yellow', to: 'red' },
{ name: 'calm', from: 'red', to: 'yellow' },
{ name: 'clear', from: 'yellow', to: 'green' }
],
callbacks: {
onpanic: function(event, from, to, msg) { alert('panic! ' + msg); },
onwarn: function(event, from, to, msg) { alert('warn! ' + msg); },
onclear: function(event, from, to, msg) { alert('thanks to ' + msg); },
ongreen: function(event, from, to) { document.body.className = 'green'; },
onyellow: function(event, from, to) { document.body.className = 'yellow'; },
onred: function(event, from, to) { document.body.className = 'red'; },
}
});
javascript实现有限状态机的更多相关文章
- JavaScript与有限状态机
有限状态机(Finite-state machine)是一个非常有用的模型,可以模拟世界上大部分事物. 简单说,它有三个特征: * 状态总数(state)是有限的. * 任一时刻,只处在一种状态之中. ...
- React源码剖析系列 - 生命周期的管理艺术
目前,前端领域中 React 势头正盛,很少能够深入剖析内部实现机制和原理.本系列文章希望通过剖析 React 源码,理解其内部的实现原理,知其然更要知其所以然. 对于 React,其组件生命周期(C ...
- FW: javascripts 要不要加引号
Javascript编程风格 http://www.ruanyifeng.com/blog/2012/04/javascript_programming_style.html 作者: 阮一峰 日期: ...
- ☀【JS】有效状态机
JavaScript与有限状态机http://www.ruanyifeng.com/blog/2013/09/finite-state_machine_for_javascript.html
- React 源码剖析系列 - 生命周期的管理艺术
目前,前端领域中 React 势头正盛,很少能够深入剖析内部实现机制和原理. 本系列文章 希望通过剖析 React 源码,理解其内部的实现原理,知其然更要知其所以然. 对于 React,其组件生命周期 ...
- 用于ASP.net的MVC模块
下载MVCBricks_SRC - 492.58 KB 表的内容 介绍系统要求游戏闪屏的最终考虑历史 介绍 自从我写上一篇关于ASP的文章以来,已经有很长时间了.净的话题.这次我决定写一些关于它的东西 ...
- 试试用有限状态机的思路来定义javascript组件
本文是一篇学习性的文章,学习利用有限状态机的思想来定义javascript组件的方法,欢迎阅读,后续计划会写几篇专门介绍自己利用有限状态机帮助自己编写组件的博客,证明这种思路对于编程实现的价值,目前正 ...
- JavaScript有限状态机实现方式
阮一峰博客 http://www.ruanyifeng.com/blog/2013/09/finite-state_machine_for_javascript.html 开源实现库(javascri ...
- JavaScript对象状态
有限状态机(Finite-state machine)是一个非常有用的模型,可以模拟世界上大部分事物. 简单说,它有三个特征: * 状态总数(state)是有限的. * 任一时刻,只处在一种状态之中. ...
随机推荐
- 百度API地图的标注不居中显示,而显示在左上角
前言:今天弄个百度地图,弄了半天就是不居中,之前使用一直没有遇到这个问题.所以就一直在找原因. 百度地图对地图所在的div做了显示隐藏之类操作,标注就不再居中显示,而显示在左上角. 查了很久,有人提出 ...
- java并发编程基础 --- 4.1线程简介
一.线程简介 什么是线程: 现在操作系统在运行一个程序时,会为其创建一个进程.例如,启动一个java程序,操作系统就会创建一个java进程.现代操作系统调度的最小单元是线程,也叫轻量级进程,在一个进程 ...
- [bzoj1497][NOI2006]最大获利_网络流_最小割
最大获利 bzoj-1497 题目大意:可以建立一个点,花费一定的代价:将已经建立的两个点之间连边,得到一定收益.有些节点之间是不允许连边的. 注释:1<=点数<=5,000,1<= ...
- Tomcat服务器的常用配置
1.如何修改端口号, tomcat启动后经常会报端口冲突, 怎么办 如果部署在Linux环境下面, 首先使用netstat -apn命令检查是否是真的端口已经被占用了 如果真的被占用,进入tomcat ...
- js网页判断移动终端浏览器版本信息是安卓还是苹果ios,判断在微信浏览器跳转不同页面,生成二维码
一个二维码,扫描进入网页,自动识别下载苹果和安卓客户端,判断网页如下,(只有苹果的微信不能自动跳转)所以加个微信判断. <!DOCTYPE html> <html> <h ...
- 基于python的接口自动化测试+ddt数据驱动
在测试接口时,一个接口会先写好测试用例,这个用例主要针对功能,传入参时考虑到各种场景,正常的,异常的,如:参数缺省,参数传一个六位数字写用例时考虑边界情况等. 一个接口设计用例时有可能会十几条到几十条 ...
- 20162323周楠《Java程序设计与数据结构》第六周总结
学号 2016-2017-2 <程序设计与数据结构>第六周学习总结 教材学习内容总结 继承:从已有类派生一个新类,是面向对象程序设计的一个特点 在Java中只支持单继承,不支持多继承 继承 ...
- swift 编写欢迎界面-- ios开发
转载自:http://blog.csdn.net/u014455765/article/details/49622947 现在很多iOS开发人员都从oc转向Swift, swift 也必将成为ios开 ...
- 20145237 实验五《Java网络编程》
20145237 实验五<Java网络编程> 一.实验内容 •1.运行下载的TCP代码,结对进行,一人服务器,一人客户端: •2.利用加解密代码包,编译运行代码,一人加密,一人解密: •3 ...
- fread函数详解
函数原型: size_t fread( void *buffer, size_t size, size_t count, FILE *stream ) b ...