学习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 建设银行登陆键盘
Jun 10 Javascript
本地图片预览(支持IE6/IE7/IE8/Firefox3)经验总结
Mar 25 Javascript
JS获取地址栏参数的小例子
Aug 23 Javascript
JS获取URL中参数值(QueryString)的4种方法分享
Apr 12 Javascript
jquery中each方法示例和常用选择器
Jul 08 Javascript
javascript实现自动输出文本(打字特效)
Aug 27 Javascript
Node.js的Web模板引擎ejs的入门使用教程
Jun 06 Javascript
深入解析js轮播插件核心代码的实现过程
Apr 14 Javascript
get  post jsonp三种数据交互形式实例详解
Aug 25 Javascript
js实现可以点击收缩或张开的悬浮窗
Sep 18 Javascript
vue.js图片转Base64上传图片并预览的实现方法
Aug 02 Javascript
vue-cli脚手架的.babelrc文件用法说明
Sep 11 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数组中删除元素的实现代码
2012/06/22 PHP
PHP判断表单复选框选中状态完整例子
2014/06/24 PHP
PHP获取文件扩展名的4种方法
2015/11/24 PHP
PHP Echo字符串的连接格式
2016/03/07 PHP
Prototype使用指南之hash.js
2007/01/10 Javascript
js window.onload 加载多个函数和追加函数详解
2014/01/08 Javascript
jQuery表单事件实例代码分享
2016/08/18 Javascript
微信小程序 MD5加密登录密码详解及实例代码
2017/01/12 Javascript
javascript 网页进度条简单实例
2017/02/22 Javascript
js简单实现网页换肤功能
2017/04/07 Javascript
underscore之Collections_动力节点Java学院整理
2017/07/10 Javascript
JS中Map和ForEach的区别
2018/02/05 Javascript
vue拦截器实现统一token,并兼容IE9验证功能
2018/04/26 Javascript
在微信小程序里使用watch和computed的方法
2018/08/02 Javascript
vue 循环加载数据并获取第一条记录的方法
2018/09/26 Javascript
微信小程序实现带缩略图轮播效果
2018/11/04 Javascript
在微信小程序中渲染HTML内容3种解决方案及分析与问题解决
2020/01/12 Javascript
[02:06]2018完美世界全国高校联赛秋季赛开始报名(附彩蛋)
2018/09/03 DOTA
python中urllib.unquote乱码的原因与解决方法
2017/04/24 Python
python线程池threadpool使用篇
2018/04/27 Python
对Python+opencv将图片生成视频的实例详解
2019/01/08 Python
python实现对图片进行旋转,放缩,裁剪的功能
2019/08/07 Python
浅谈Python类中的self到底是干啥的
2019/11/11 Python
python自动识别文本编码格式代码
2019/12/26 Python
python实现飞行棋游戏
2020/02/05 Python
详解python定时简单爬取网页新闻存入数据库并发送邮件
2020/11/27 Python
澳大利亚Rockwear官网:女子瑜伽、健身和运动服
2021/01/26 全球购物
家长给幼儿园的表扬信
2014/01/09 职场文书
远程研修随笔感言
2014/02/10 职场文书
物业管理委托协议(2篇)
2014/09/23 职场文书
收款委托书
2014/10/14 职场文书
2014年学校食堂工作总结
2014/11/25 职场文书
七年级作文之冬景
2019/11/07 职场文书
详解JavaScript中的执行上下文及调用堆栈
2021/04/29 Javascript
《吸血鬼:避世 血猎》官宣4.27发售 系列首款大逃杀
2022/04/03 其他游戏
微信小程序APP页面的之间的相互传递参数以及自定义组件
2022/04/19 Javascript