轻松掌握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 相关文章推荐
Fixie.js 自动填充内容的插件
Jun 28 Javascript
JQuery.Ajax()的data参数类型实例详解
Nov 20 Javascript
jquery事件与绑定事件
Mar 16 Javascript
jQuery插件FusionCharts绘制的2D帕累托图效果示例【附demo源码】
Mar 28 jQuery
基于Vue2实现的仿手机QQ单页面应用功能(接入聊天机器人 )
Mar 30 Javascript
详解微信小程序 相对定位和绝对定位
May 11 Javascript
AngularJS学习笔记之表单验证功能实例详解
Jul 06 Javascript
浅谈angular2 组件的生命周期钩子
Aug 12 Javascript
jQuery实现对网页节点的增删改查功能示例
Sep 18 jQuery
Validform验证时可以为空否则按照指定格式验证
Oct 20 Javascript
JavaScript实现横版菜单栏
Mar 17 Javascript
Js Snowflake(雪花算法)生成随机ID的实现方法
Aug 26 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
打造计数器DIY三步曲(上)
2006/10/09 PHP
PHP数据库链接类(PDO+Access)实例分享
2013/12/05 PHP
通过curl模拟post和get方式提交的表单类
2014/04/23 PHP
PHP实现支持加盐的图片加密解密
2016/09/09 PHP
PHP7新增运算符用法实例分析
2016/09/26 PHP
javascript 一个函数对同一元素的多个事件响应
2009/07/25 Javascript
javascript跟随滚动效果插件代码(javascript Follow Plugin)
2013/08/03 Javascript
javascript间隔刷新的简单实例
2013/11/14 Javascript
js动态修改整个页面样式达到换肤效果
2014/05/23 Javascript
用队列模拟jquery的动画算法实例
2015/01/20 Javascript
ionic实现带字的toggle滑动组件
2016/08/27 Javascript
Easyui Tree获取当前选择节点的所有顶级父节点
2017/02/14 Javascript
Bootstrap 3多级下拉菜单实例
2017/11/23 Javascript
关于HTTP传输中gzip压缩的秘密探索分析
2018/01/12 Javascript
微信小程序基于movable-view实现滑动删除效果
2020/01/08 Javascript
Vue环境搭建+VSCode+Win10的详细教程
2020/08/19 Javascript
OpenLayers加载缩放控件使用方法详解
2020/09/25 Javascript
利用python发送和接收邮件
2016/09/27 Python
浅谈Python由__dict__和dir()引发的一些思考
2017/10/30 Python
python模块smtplib实现纯文本邮件发送功能
2018/05/22 Python
python使用mitmproxy抓取浏览器请求的方法
2019/07/02 Python
详解Python 字符串相似性的几种度量方法
2019/08/29 Python
使用python图形模块turtle库绘制樱花、玫瑰、圣诞树代码实例
2020/03/16 Python
夏威夷航空官网:Hawaiian Airlines
2016/09/11 全球购物
社区活动邀请函范文
2014/01/29 职场文书
优秀经理事迹材料
2014/02/01 职场文书
师范教师专业大学生职业生涯规划范文
2014/03/02 职场文书
合作意向书
2014/07/30 职场文书
公司优秀员工获奖感言
2014/08/14 职场文书
上班迟到检讨书300字
2014/10/18 职场文书
归元寺导游词
2015/02/06 职场文书
幼儿园安全工作总结2015
2015/04/20 职场文书
2015年园林绿化工作总结
2015/05/23 职场文书
2016春季运动会通讯稿
2015/07/18 职场文书
浅谈Python魔法方法
2021/06/28 Java/Android
java解析XML详解
2021/07/09 Java/Android