学习JavaScript设计模式之状态模式


Posted in Javascript onJanuary 08, 2016

状态模式的关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变。

当电灯开着,此时按下开关,电灯会切换到关闭状态;再按一次开关,电灯又将被打开。同一个开关在不同的状态下,表现出来的行为是不一样的。

一、有限状态机

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

允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
解释:
(1)将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态发生改变时,会带来不同的行为变化。
(2)使用的对象,在不同的状态下具有截然不同的行为(委托效果)

谈到封装,一般优先考虑封装对象的行为,而不是对象的状态。
但在状态模式中刚好相反,状态模式的关键是把事物的每种状态都封装成单独的类。

二、示例

点灯程序 (弱光 ?> 强光 ?> 关灯)循环

// 关灯
var OffLightState = function(light) {
  this.light = light;
};
// 弱光
var OffLightState = function(light) {
  this.light = light;
};
// 强光
var StrongLightState = function(light) {
  this.light = light;
};

var Light = function(){
  /* 开关状态 */
  this.offLight = new OffLightState(this);
  this.weakLight = new WeakLightState(this);
  this.strongLight = new StrongLightState(this);
  /* 快关按钮 */
  this.button = null;
};
Light.prototype.init = function() {
  var button = document.createElement("button"),
    self = this;
  this.button = document.body.appendChild(button);
  this.button.innerHTML = '开关';
  this.currentState = this.offLight;
  this.button.click = function() {
    self.currentState.buttonWasPressed();
  }
};
// 让抽象父类的抽象方法直接抛出一个异常(避免状态子类未实现buttonWasPressed方法)
Light.prototype.buttonWasPressed = function() {
  throw new Error("父类的buttonWasPressed方法必须被重写");
};
Light.prototype.setState = function(newState) {
  this.currentState = newState;
};

/* 关灯 */
OffLightState.prototype = new Light(); // 继承抽象类
OffLightState.prototype.buttonWasPressed = function() {
  console.log("关灯!");
  this.light.setState(this.light.weakLight);
}
/* 弱光 */
WeakLightState.prototype = new Light();
WeakLightState.prototype.buttonWasPressed = function() {
  console.log("弱光!");
  this.light.setState(this.light.strongLight);
};
/* 强光 */
StrongLightState.prototype = new Light();
StrongLightState.prototype.buttonWasPressed = function() {
  console.log("强光!");
  this.light.setState(this.light.offLight);
};

PS:说明补充
必须把OffLightState、WeakLightState、StrongLightState构造函数提前。

new A("a");
var A = function(a) {
  console.log(a)
}

new B("b");
function B(b) {
  console.log(b);
}

函数声明会被提升到普通变量之前。

三、性能优化点

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

四、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();

希望本文所述对大家学习javascript程序设计有所帮助。

Javascript 相关文章推荐
初识javascript 文档碎片
Jul 13 Javascript
jquery easyui combobox模糊过滤(示例代码)
Nov 30 Javascript
jqplot通过ajax动态画折线图的方法及思路
Dec 08 Javascript
jQuery调用RESTful WCF示例代码(GET方法/POST方法)
Jan 26 Javascript
javascript中的nextSibling使用陷(da)阱(keng)
May 05 Javascript
一个极为简单的requirejs实现方法
Oct 20 Javascript
函数四种调用模式以及其中的this指向
Jan 16 Javascript
vue构建单页面应用实战
Apr 10 Javascript
jQuery中hover方法搭配css的hover选择器,实现选中元素突出显示方法
May 08 jQuery
浅析Vue 和微信小程序的区别、比较
Aug 03 Javascript
微信小程序生成海报分享朋友圈的实现方法
May 06 Javascript
原生js实现无缝轮播图效果
Jan 28 Javascript
jQuery CSS3相结合实现时钟插件
Jan 08 #Javascript
js实现对ajax请求面向对象的封装
Jan 08 #Javascript
javascript弹性运动效果简单实现方法
Jan 08 #Javascript
javascript运动效果实例总结(放大缩小、滑动淡入、滚动)
Jan 08 #Javascript
javascript运动框架用法实例分析(实现放大与缩小效果)
Jan 08 #Javascript
jquery实现简单的遮罩层
Jan 08 #Javascript
javascript多物体运动实现方法分析
Jan 08 #Javascript
You might like
php 无限分类的树类代码
2009/12/03 PHP
使用PHP下载CSS文件中的图片的代码
2013/09/24 PHP
php实现的ping端口函数实例
2014/11/12 PHP
php实现session自定义会话处理器的方法
2015/01/27 PHP
php+MySQL实现登录时验证登录名和密码是否正确
2016/05/10 PHP
PHP7匿名类用法分析
2016/09/26 PHP
PHP智能识别收货地址信息实例
2019/01/05 PHP
在b/s开发中经常用到的javaScript技术
2006/08/23 Javascript
javascript实现的在当前窗口中漂浮框的代码
2010/03/15 Javascript
jQuery ajax(复习)—Baidu ajax request分离版
2013/01/24 Javascript
js选择并转移导航菜单示例代码
2014/08/19 Javascript
JS脚本根据手机浏览器类型跳转WAP手机网站(两种方式)
2015/08/04 Javascript
JQuery异步加载PartialView的方法
2016/06/07 Javascript
利用Angularjs和原生JS分别实现动态效果的输入框
2016/09/01 Javascript
ajax 提交数据到后台jsp页面及页面跳转问题
2017/01/19 Javascript
又一款MVVM组件 构建自己的Vue组件(2)
2017/03/13 Javascript
AngularJS中下拉框的高级用法示例
2017/10/11 Javascript
js原生日历的实例(推荐)
2017/10/31 Javascript
简述vue状态管理模式之vuex
2018/08/29 Javascript
vue2.0获取鼠标位置的方法
2018/09/13 Javascript
element-ui带输入建议的input框踩坑(输入建议空白以及会闪出上一次的输入建议问题)
2019/01/15 Javascript
详解ES7 Decorator 入门解析
2019/02/18 Javascript
如何使用50行javaScript代码实现简单版的call,apply,bind
2019/08/14 Javascript
javascript设计模式 ? 中介者模式原理与用法实例分析
2020/04/20 Javascript
[01:21:58]守擂赛DOTA2第一周决赛
2020/04/22 DOTA
python 对dataframe下面的值进行大规模赋值方法
2018/06/09 Python
django实现HttpResponse返回json数据为中文
2020/03/27 Python
Python中内建模块collections如何使用
2020/05/27 Python
CSS3+js实现简单的时钟特效
2015/03/18 HTML / CSS
HTML5 canvas标签实现刮刮卡效果
2015/04/24 HTML / CSS
地球鞋加拿大官网:Earth Shoes Canada
2020/11/17 全球购物
用缩写的指针比较"if(p)" 检查空指针是否可靠?如果空指针的内部表达不是0会怎么样?
2014/01/05 面试题
计算机应用与科学个人的自我评价
2013/11/15 职场文书
中英文自我评价语句
2013/12/20 职场文书
幼儿园见习报告范文
2014/10/30 职场文书
2015年国庆节活动总结
2015/03/23 职场文书