轻松掌握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 相关文章推荐
基于Asp.net与Javascript控制的日期控件
May 22 Javascript
22点关于jquery性能优化的建议
May 28 Javascript
深入理解JavaScript编程中的原型概念
Jun 25 Javascript
基于JavaScript制作霓虹灯文字 代码 特效
Sep 01 Javascript
JavaScript中获取Radio被选中的值
Nov 11 Javascript
js实现网页的两个input标签内的数值加减(示例代码)
Aug 15 Javascript
vue仿淘宝订单状态的tab切换效果
Jun 23 Javascript
bootstrap treeview 扩展addNode方法动态添加子节点的方法
Nov 21 Javascript
vue2.0项目实现路由跳转的方法详解
Jun 21 Javascript
JS实现获取毫秒值及转换成年月日时分秒的方法
Aug 15 Javascript
django js 实现表格动态标序号的实例代码
Jul 12 Javascript
JS实现TITLE悬停长久显示效果完整示例
Feb 11 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
制作美丽的拉花
2021/03/03 冲泡冲煮
snoopy 强大的PHP采集类使用实例代码
2010/12/09 PHP
php入门之连接mysql数据库的一个类
2012/04/21 PHP
php向js函数传参的几种方法
2014/08/10 PHP
微信公众平台开发关注及取消关注事件的方法
2014/12/23 PHP
WordPress中用于获取文章信息以及分类链接的函数用法
2015/12/18 PHP
Yii操作数据库实现动态获取表名的方法
2016/03/29 PHP
初识ThinkPHP控制器
2016/04/07 PHP
thinkphp利用模型通用数据编辑添加和删除的实例代码
2016/11/20 PHP
使用PHPUnit进行单元测试并生成代码覆盖率报告的方法
2019/03/08 PHP
laravel5 Eloquent 实现事务方式
2019/10/21 PHP
Mootools 1.2教程 类(一)
2009/09/15 Javascript
判断对象是否Window的实现代码
2012/01/10 Javascript
通过实例理解javascript中没有函数重载的概念
2015/06/03 Javascript
jQuery实现弹出窗口中切换登录与注册表单
2015/06/05 Javascript
js+html5实现canvas绘制镂空字体文本的方法
2015/06/05 Javascript
微信小程序 video组件详解
2016/10/25 Javascript
解决前端跨域问题方案汇总
2016/11/20 Javascript
浅谈html转义及防止javascript注入攻击的方法
2016/12/04 Javascript
基于JS实现二维码图片固定在右下角某处并跟随滚动条滚动
2017/02/08 Javascript
vue生成随机验证码的示例代码
2017/09/29 Javascript
解决一个微信号同时支持多个环境网页授权问题
2019/08/07 Javascript
微信小程序开发之map地图组件定位并手动修改位置偏差
2019/08/17 Javascript
JS实现密码框效果
2020/09/10 Javascript
three.js中多线程的使用及性能测试详解
2021/01/07 Javascript
[03:06]2018年度CS GO最具人气解说-完美盛典
2018/12/16 DOTA
用Python展示动态规则法用以解决重叠子问题的示例
2015/04/02 Python
解决python selenium3启动不了firefox的问题
2018/10/13 Python
解决python中使用PYQT时中文乱码问题
2019/06/17 Python
Python实现最常见加密方式详解
2019/07/13 Python
解决python3中os.popen()出错的问题
2020/11/19 Python
军训学生自我鉴定
2014/02/12 职场文书
园艺师求职信
2014/03/10 职场文书
教育实习指导教师评语
2014/12/31 职场文书
小学主题班会教案
2015/08/17 职场文书
Windows server 2003卸载和安装IIS的图文教程
2022/07/15 Servers