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)是有限的. * 任一时刻,只处在一种状态之中. ...
随机推荐
- iOS scrollView中嵌套多个tabeleView处理方案
项目中经常会有这样的需求,scrollView有个头部,当scrollView滚动的时候头部也跟着滚动,同时头部还有一个tab会锁定在某个位置,scrollView中可以放很多不同的view,这些vi ...
- python函数知识点(详解匿名函数)
Python函数是组织好的.单一的.具有独立功能模块的代码块. 函数能提高应用的模块性,和代码的重复利用率.Python提供了许多内建函数,比如print().但你也可以自己创建函数,这被叫做用户自定 ...
- Java多线程:CopyOnWrite容器
一.什么是CopyOnWrite容器 CopyOnWrite容器即写时复制的容器.通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然 ...
- UI线程异常处理方法
当应用程序启动,创建了一个叫“main”的线程,用于管理UI相关,又叫UI线程.其他线程叫工作线程(Work Thread). Single Thread Model 一个组件的创建并不会新建一个线程 ...
- HTTP协议----请求方法和状态码
现在广泛使用的是HTTP/1.1版本,发布于1997年. 理解HTTP协议,首先从请求开始,比如: POST /form/entry HTTP/1.1 格式为: 请求方法 URI 协议版本 请求方法: ...
- 移动前端的html5 head 头标签
DOCTYPE DOCTYPE(Document Type),该声明位于文档中最前面的位置,处于 html 标签之前,此标签告知浏览器文档使用哪种 HTML 或者 XHTML 规范. 使用 HTML5 ...
- 使用 win10 的正确姿势 (第二版)
文章为本人原创,转载请注明出处,谢谢. 17年9月初,写了第一篇<使用 win10 的正确姿势>,而现在半年多过去,文章更新了一些,主要是桌面的变化. 一. 重新定义桌面 我的桌面: 将桌 ...
- [BZOJ 2064]分裂
2064: 分裂 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 572 Solved: 352[Submit][Status][Discuss] De ...
- DFA算法的简单说明!
1.DFA算法简介 DFA全称为:Deterministic Finite Automaton,即确定有穷自动机.其特征为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号 ...
- ZJOI2018游记
我是一只普及组的菜鸡,我很菜 我参加 \(ZJOI\) 只是来试试水(水好深啊~),看看大佬(差距好大啊~),以后要好好学习 \(day0\) 下午2:00,颁奖 还以为要到很晚,还是挺快的 \(da ...