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实现有限状态机的更多相关文章

  1. JavaScript与有限状态机

    有限状态机(Finite-state machine)是一个非常有用的模型,可以模拟世界上大部分事物. 简单说,它有三个特征: * 状态总数(state)是有限的. * 任一时刻,只处在一种状态之中. ...

  2. React源码剖析系列 - 生命周期的管理艺术

    目前,前端领域中 React 势头正盛,很少能够深入剖析内部实现机制和原理.本系列文章希望通过剖析 React 源码,理解其内部的实现原理,知其然更要知其所以然. 对于 React,其组件生命周期(C ...

  3. FW: javascripts 要不要加引号

    Javascript编程风格  http://www.ruanyifeng.com/blog/2012/04/javascript_programming_style.html 作者: 阮一峰 日期: ...

  4. ☀【JS】有效状态机

    JavaScript与有限状态机http://www.ruanyifeng.com/blog/2013/09/finite-state_machine_for_javascript.html

  5. React 源码剖析系列 - 生命周期的管理艺术

    目前,前端领域中 React 势头正盛,很少能够深入剖析内部实现机制和原理. 本系列文章 希望通过剖析 React 源码,理解其内部的实现原理,知其然更要知其所以然. 对于 React,其组件生命周期 ...

  6. 用于ASP.net的MVC模块

    下载MVCBricks_SRC - 492.58 KB 表的内容 介绍系统要求游戏闪屏的最终考虑历史 介绍 自从我写上一篇关于ASP的文章以来,已经有很长时间了.净的话题.这次我决定写一些关于它的东西 ...

  7. 试试用有限状态机的思路来定义javascript组件

    本文是一篇学习性的文章,学习利用有限状态机的思想来定义javascript组件的方法,欢迎阅读,后续计划会写几篇专门介绍自己利用有限状态机帮助自己编写组件的博客,证明这种思路对于编程实现的价值,目前正 ...

  8. JavaScript有限状态机实现方式

    阮一峰博客 http://www.ruanyifeng.com/blog/2013/09/finite-state_machine_for_javascript.html 开源实现库(javascri ...

  9. JavaScript对象状态

    有限状态机(Finite-state machine)是一个非常有用的模型,可以模拟世界上大部分事物. 简单说,它有三个特征: * 状态总数(state)是有限的. * 任一时刻,只处在一种状态之中. ...

随机推荐

  1. Python第二话 初识复杂数据类型(list、dictionary、tuple)

    上一篇我们简单认识了数据类型:数字number和字符串string,这篇我们就来隆重介绍一下重量级的数据类型:列表list.字典dictionary和元组tuple. 一.列表List: ①列表是什么 ...

  2. Mybatis 常用标签

    MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦.拼接的时候要确保不能忘了必要的空格,还要注意省掉 ...

  3. (译文)学习ES6非常棒的特性-深入研究var, let and const

    Var var firstVar; //firstVar被声明,它的默认值是undefined var secondVar = 2; //secondVar被声明,被赋值2 先看一个例子: var i ...

  4. geotrellis使用(三十六)瓦片入库更新图层

    前言 Geotrellis 是针对大数据量栅格数据进行分布式空间计算的框架,这一点毋庸置疑,并且无论采取何种操作,其实都是先将大块的数据切割成一定大小的小数据(专业术语为瓦片),这是分治的思想,也是分 ...

  5. C语言第一周作业

    题目一:7-3 温度转换 本题要求编写程序,计算华氏温度150°F对应的摄氏温度.计算公式:C=5×(F−32)/9,式中:C表示摄氏温度,F表示华氏温度,输出数据要求为整型. 1.实验代码 2.设计 ...

  6. Java作业-数据库

    本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结与数据库相关内容. 在Java中使用数据库要经过以下几个步骤: 1. 注册 JDBC 驱动 Class.forName("com ...

  7. C语言--第六周作业

    一.高速公路超速罚款 1.代码 #include<stdio.h> int main() { int a,b; float c; scanf("%d %d",& ...

  8. 学号:201621123032 《Java程序设计》第2周学习总结

    1: 本周学习总结 本周学习java的数据类型,两种数据类型:基本数据类型和引用数据类型. 学习关于String和StringBuilder之间不同. 本周还学习数组.一维数组,多维数组,和动态数组. ...

  9. Flask 学习 十 博客文章

    提交和显示博客文章 app/models.py 文章模型 class Post(db.Model): __tablename__ = 'posts' id = db.Column(db.Integer ...

  10. python利用twilio模块给自己发短信

    1.访问http://twilio.com/并填写注册表单.注册了新账户后,你需要验证一个手机号码,短信将发给该号码. 2.Twilio 提供的试用账户包括一个电话号码,它将作为短信的发送者.你将需要 ...