轻松掌握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 相关文章推荐
javascript写的日历类(基于pj)
Dec 28 Javascript
javascript显示用户停留时间的简单实例
Aug 05 Javascript
禁止ajax缓存获取程序最新数据的方法
Nov 19 Javascript
js插件YprogressBar实现漂亮的进度条效果
Apr 20 Javascript
基于javascript简单实现对身份证校验
Jan 25 Javascript
js插件dropload上拉下滑加载数据实例解析
Jul 27 Javascript
如何使用headjs来管理和异步加载js
Nov 29 Javascript
Bootstrap免费字体和图标网站(值得收藏)
Mar 16 Javascript
微信小程序 setData的使用方法详解
Apr 20 Javascript
json前后端数据交互相关代码
Sep 19 Javascript
详解Webstorm 下的Angular2.0开发之路(图文)
Dec 06 Javascript
JS实现烟花爆炸效果
Mar 10 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
日本十大惊悚动漫
2020/03/04 日漫
php桌面中心(三) 修改数据库
2007/03/11 PHP
php中比较简单的导入phpmyadmin生成的sql文件的方法
2011/06/28 PHP
解析Win7 XAMPP apache无法启动的问题
2013/06/26 PHP
纯PHP生成的一个树叶图片画图例子
2014/04/16 PHP
用js怎么把&amp;字符换成&quot;&amp;amp:&quot;
2006/10/19 Javascript
理解 JavaScript 预解析
2009/10/25 Javascript
表单元素与非表单元素刷新区别详细解析
2013/11/06 Javascript
两种不同的方法实现js对checkbox进行全选和反选
2014/05/13 Javascript
Javascript判断图片尺寸大小实例分析
2014/06/16 Javascript
JS实现文字放大效果的方法
2015/03/03 Javascript
Javascript表单特效之十大常用原理性样例代码大总结
2016/07/12 Javascript
QRCode.js:基于JQuery的生成二维码JS库的使用
2017/06/23 jQuery
webpack组织模块打包Library的原理及实现
2018/03/10 Javascript
Vue加载组件、动态加载组件的几种方式
2018/08/31 Javascript
vue自定义键盘信息、监听数据变化的方法示例【基于vm.$watch】
2019/03/16 Javascript
vue.js click点击事件获取当前元素对象的操作
2020/08/07 Javascript
[03:03]DOTA2校园争霸赛 济南城市决赛欢乐发奖活动
2013/10/21 DOTA
[43:43]完美世界DOTA2联赛PWL S2 FTD.C vs Rebirth 第一场 11.22
2020/11/24 DOTA
Python中MYSQLdb出现乱码的解决方法
2014/10/11 Python
python获取指定目录下所有文件名列表的方法
2015/05/20 Python
python3大文件解压和基本操作
2017/12/15 Python
Django开发的简易留言板案例详解
2018/12/04 Python
python lambda表达式(匿名函数)写法解析
2019/09/16 Python
Python迭代器iterator生成器generator使用解析
2019/10/24 Python
python os.path.isfile 的使用误区详解
2019/11/29 Python
Matplotlib 绘制饼图解决文字重叠的方法
2020/07/24 Python
详解Python中Pyyaml模块的使用
2020/10/08 Python
莫斯科大型旅游休闲商品超市:Camping.ru
2020/09/16 全球购物
大学活动策划书范文
2014/01/10 职场文书
庆元旦迎新年广播稿
2014/02/18 职场文书
《学棋》教后反思
2014/04/14 职场文书
绿色出行口号
2014/06/18 职场文书
新兵入伍心得体会
2014/09/04 职场文书
电影红河谷观后感
2015/06/11 职场文书
win10下go mod配置方式
2021/04/25 Golang