轻松掌握JavaScript状态模式


Posted in Javascript onSeptember 07, 2016

状态模式 

状态模式(State)允许一个对象在其内部状态改变的时候改变它的行为,对象看起来似乎修改了它的类。 

状态模式的使用场景也特别明确,有如下两点:
 1.一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。(有些对象通常会有好几个状态,在每个状态都只可以做当前状态才可以做的事情,而不能做其它状态能做的事儿)

 2.一个操作中含有大量的分支语句,而且这些分支语句依赖于该对象的状态。状态通常为一个或多个枚举常量的表示。 

一、有限状态机

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

通用做法:将状态封装成独立的类(状态机),并将请求委托给当前的状态对象,当对象的内部状态发生改变时,会带来不同的行为变化。 

二、性能优化点

 1.如何管理状态对象的创建和销毁?第一种仅当state对象被需要时才创建并随后销毁(state对象比较庞大,优先选择), 另一种是一开始就创建好所有的状态对象,并且始终不销毁它们(状态改变频繁)。
 2.利用享元模式共享一个state对象。 

举个稍微复杂的例子,相信大家都玩过角色扮演类游戏,里面的角色就有很多种状态(站、走、跑、跳、蹲等),各个状态之间的切换是被规定好了的,且任何时刻都只能处于一种状态中,而在每个状态下,角色只能做当前状态下被允许的行为(如:普通攻击、各种技能攻击、防御等) 

这是我写的移动小球的例子:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title></title>
 <script>
  window.onload = function() {
   var FSM = {
    show1: {
     clickBtn: function (key) {
      change.call(this,key);
     }
    },
    show2: {
     clickBtn: function (key) {
      change.call(this,key);
     }
    },
    show3: {
     clickBtn: function (key) {
      change.call(this,key);
     }
    },
    show4: {
     clickBtn: function (key) {
      change.call(this,key);
     }
    }
   };
   var Ball = function () {
    this.curentState = FSM.show1;
    this.div = null;
   };
   Ball.prototype.init = function () {
    var self = this;
    this.div = document.getElementById('go');
    document.body.onkeydown = function (event) {
     var key = event.keyCode;
     self.curentState.clickBtn.call(self,key);
    }
   };
   function change(key){
    var styles = window.getComputedStyle(this.div),
     parentStyles = window.getComputedStyle(this.div.parentNode),
     top = parseInt(styles.top),
     left = parseInt(styles.left);
    if(key === 40){
     top += (top+parseInt(styles.height))<parseInt(parentStyles.height) ? 10 : 0;
     this.div.style.top = top+'px';
     this.curentState = FSM.show3;
    }
    if(key === 38){
     top -= (top > 0 ? 10 : 0);
     this.div.style.top = top+'px';
     this.curentState = FSM.show4;
    }
    if(key === 37){
     left -= (left > 0 ? 10 : 0);
     this.div.style.left = left+'px';
     this.curentState = FSM.show1;
    }
    if(key === 39){
     this.curentState = FSM.show2;
     left += (left+parseInt(styles.width))<parseInt(parentStyles.width) ? 10 : 0;
     this.div.style.left = left+'px';
    }
   }
   var a = new Ball();
   a.init();
  }
 </script>
 <style>
  #div{
   position: absolute;
   width: 80%;
   height: 80%;
   top: 0;
   bottom: 0;
   left: 0;
   right: 0;
   margin: auto;
   border: 1px solid darkcyan;
  }
  #go{
   position:absolute;
   width:50px;
   height:50px;
   left: 10px;
   top:20px;
   border:1px solid gray;
   -webkit-border-radius : 50px;
   -moz-border-radius: 50px;
   border-radius: 50px;
   background-image: radial-gradient(circle, white 5%, black 100%);
  }
 </style>
</head>
<body>
<div id="div">按下方向键移动方块
 <div id="go"></div>
</div>
</body>
</html>

三、JavaScript版本的状态机(以简单的开关灯为例)

 1.通过Function.prototype.call方法直接把请求委托给某个字面量对象来执行

// 状态机
var FSM = {
 off: {
 buttonWasPressed: function() {
  console.log("关灯");
  this.button.innerHTML = "下一次按我是开灯"; // 这是Light上的属性!!!
  this.currState = FSM.on;   // 这是Light上的属性!!!
 }
 },
 on: {
 buttonWasPressed: function() {
  console.log("开灯");
  this.button.innerHTML = "下一次按我是关灯";
  this.currState = FSM.off;
 }
 },
};
 
var Light = function() {
 this.currState = FSM.off; // 设置当前状态
 this.button = null;
};
 
Light.prototype.init = function() {
 var button = document.createElement("button");
 self = this;
 
 button.innerHTML = "已关灯";
 this.button = document.body.appendChild(button);
 this.button.onclick = function() {
 // 请求委托给FSM状态机
 self.currState.buttonWasPressed.call(self);
 }
 
}
 
var light = new Light();
light.init();

2.利用delegate函数

var delegate = function(client, delegation) {
 return {
 buttonWasPressed: function() {
  return delegation.buttonWasPressed.apply(client, arguments);
 }
 };
};
 
// 状态机
var FSM = {
 off: {
 buttonWasPressed: function() {
  console.log("关灯");
  this.button.innerHTML = "下一次按我是开灯";
  this.currState = this.onState;
 }
 },
 on: {
 buttonWasPressed: function() {
  console.log("开灯");
  this.button.innerHTML = "下一次按我是关灯";
  this.currState = this.offState;
 }
 },
};
 
var Light = function() {
 this.offState = delegate(this, FSM.off);
 this.onState = delegate(this, FSM.on);
 this.currState = this.offState; // 设置当前状态
 this.button = null;
};
 
Light.prototype.init = function() {
 var button = document.createElement("button");
 self = this;
 
 button.innerHTML = "已关灯";
 this.button = document.body.appendChild(button);
 this.button.onclick = function() {
 // 请求委托给FSM状态机
 self.currState.buttonWasPressed();
 }
}
 
var light = new Light();
light.init();

状态模式和策略模式很像,它们都封装了一系列的算法或行为,它们都有一个上下文对象来把请求委托给封装类(策略类、状态机),但它们的意图不同:
 1.策略类的各个属性之间是平等平行的,它们之间没有任何联系
 2.状态机中的各个状态之间存在相互切换,且是被规定好了的。

参考文献: 《JavaScript模式》 《JavaScript设计模式与开发实践》

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
[原创]静态页面也可以实现预览 列表不同的显示方式
Oct 14 Javascript
JS与C#编码解码
Dec 03 Javascript
jQuery点击自身以外地方关闭弹出层的简单实例
Dec 24 Javascript
js post提交调用方法
Feb 12 Javascript
js子页面获取父页面数据示例
May 15 Javascript
js实现仿QQ秀换装效果的方法
Mar 04 Javascript
深入理解jQuery 事件处理
Jun 14 Javascript
ES6入门教程之Class和Module详解
May 17 Javascript
Vue DevTools调试工具的使用
Dec 05 Javascript
vue侧边栏动态生成下级菜单的方法
Sep 07 Javascript
iview同时验证多个表单问题总结
Sep 29 Javascript
微信小程序实现点击效果
Jun 21 Javascript
JS简单实现tab切换效果的多窗口显示功能
Sep 07 #Javascript
JS实现的幻灯片切换显示效果
Sep 07 #Javascript
javascript宿主对象之window.navigator详解
Sep 07 #Javascript
Angular 理解module和injector,即依赖注入
Sep 07 #Javascript
JS继承之借用构造函数继承和组合继承
Sep 07 #Javascript
Node.js读写文件之批量替换图片的实现方法
Sep 07 #Javascript
jQuery实现底部浮动窗口效果
Sep 07 #Javascript
You might like
php实现12306火车票余票查询和价格查询(12306火车票查询)
2014/01/14 PHP
ThinkPHP中create()方法自动验证表单信息
2017/04/28 PHP
PHP INT类型在内存中占字节详解
2019/07/20 PHP
JS启动应用程序的一个简单例子
2008/05/11 Javascript
javascript 获取模态窗口的滚动位置代码
2013/08/06 Javascript
jQuery选择器简明总结(含用法实例,一目了然)
2014/04/25 Javascript
JS实现可缩放、拖动、关闭和最小化的浮动窗口完整实例
2015/03/04 Javascript
JavaScript实现的SHA-1加密算法完整实例
2016/02/02 Javascript
JS实现把鼠标放到链接上出现滚动文字的方法
2016/04/06 Javascript
AngularJS 入门教程之HTML DOM实例详解
2016/07/28 Javascript
AngularJS基础 ng-include 指令示例讲解
2016/08/01 Javascript
Node.js利用debug模块打印出调试日志的方法
2017/04/25 Javascript
react实现点击选中的li高亮的示例代码
2018/05/24 Javascript
微信小程序自定义组件实现环形进度条
2020/11/17 Javascript
从零搭一个自用的前端脚手架的方法步骤
2019/09/23 Javascript
安装ElasticSearch搜索工具并配置Python驱动的方法
2015/12/22 Python
Python部署web开发程序的几种方法
2017/05/05 Python
Python实现的破解字符串找茬游戏算法示例
2017/09/25 Python
python如何统计序列中元素
2020/07/31 Python
Python爬虫常用库的安装及其环境配置
2018/09/19 Python
PyCharm搭建Spark开发环境实现第一个pyspark程序
2019/06/13 Python
如何在Django项目中引入静态文件
2019/07/26 Python
Python高阶函数与装饰器函数的深入讲解
2020/11/10 Python
CSS3中Transition动画属性用法详解
2016/07/04 HTML / CSS
探索HTML5本地存储功能运用技巧
2016/03/02 HTML / CSS
会计专业大学生求职信范文
2014/01/28 职场文书
中学教师教育感言
2014/02/21 职场文书
保护动物的标语
2014/06/11 职场文书
住房租房协议书
2014/08/20 职场文书
材料物理专业求职信
2014/09/01 职场文书
高三毕业评语
2014/12/31 职场文书
2015年宣传工作总结
2015/04/08 职场文书
严以修身专题学习研讨会发言材料
2015/11/09 职场文书
Python常遇到的错误和异常
2021/11/02 Python
在ubuntu下安装go开发环境的全过程
2022/08/05 Golang
css之clearfix的用法深入理解(必看篇)
2023/05/21 HTML / CSS