学习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 相关文章推荐
JQuery Easyui Tree的oncheck事件实现代码
May 28 Javascript
jQuery.Validate验证库的使用介绍
Apr 26 Javascript
当json键为数字时的取值方法解析
Nov 15 Javascript
如何使用jquery easyui创建标签组件
Nov 18 Javascript
动态加载js文件简单示例
Apr 21 Javascript
Web Uploader文件上传插件使用详解
May 10 Javascript
JavaScript中apply方法的应用技巧小结
Sep 29 Javascript
使用 Node.js 开发资讯爬虫流程
Jan 07 Javascript
Vue绑定内联样式问题
Oct 17 Javascript
JavaScript实现背景自动切换小案例
Sep 27 Javascript
vue vantUI实现文件(图片、文档、视频、音频)上传(多文件)
Oct 15 Javascript
Ajax异步刷新功能及简单案例
Nov 20 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中动态HTML的输出技术
2006/10/09 PHP
Yii2使用$this->context获取当前的Module、Controller(控制器)、Action等
2017/03/29 PHP
php语法检查的方法总结
2019/01/21 PHP
thinkphp5.1 框架钩子和行为用法实例分析
2020/05/25 PHP
PHP dirname(__FILE__)原理及用法解析
2020/10/28 PHP
ASP中用Join和Array,可以加快字符连接速度的代码
2007/08/22 Javascript
使Ext的Template可以解析二层的json数据的方法
2007/12/22 Javascript
js常见表单应用技巧
2008/01/09 Javascript
JavaScript 图片预览效果 推荐
2009/12/22 Javascript
js实现字符串的16进制编码不加密
2014/04/25 Javascript
javascript实现数组内值索引随机化及创建随机数组的方法
2015/08/10 Javascript
JavaScript算法系列之快速排序(Quicksort)算法实例详解
2016/09/04 Javascript
微信小程序购物商城系统开发系列-工具篇的介绍
2016/11/21 Javascript
JS与jQuery实现子窗口获取父窗口元素值的方法
2017/04/17 jQuery
解决JQuery全选/反选第二次失效的问题
2017/10/11 jQuery
浅谈vuejs实现数据驱动视图原理
2018/02/23 Javascript
详解基于Koa2开发微信二维码扫码支付相关流程
2018/05/16 Javascript
element-ui 中使用upload多文件上传只请求一次接口
2019/07/19 Javascript
微信小程序tabBar 返回tabBar不刷新页面
2019/07/25 Javascript
在Vue项目中用fullcalendar制作日程表的示例代码
2019/08/04 Javascript
适合前端Vue开发童鞋的跨平台Weex的使用详解
2019/10/16 Javascript
javascript 设计模式之享元模式原理与应用详解
2020/04/08 Javascript
[02:47]2018年度DOTA2最佳辅助位选手4号位-完美盛典
2018/12/17 DOTA
使用Python写CUDA程序的方法
2017/03/27 Python
Python进阶_关于命名空间与作用域(详解)
2017/05/29 Python
Python Pandas找到缺失值的位置方法
2018/04/12 Python
Tensorflow实现AlexNet卷积神经网络及运算时间评测
2018/05/24 Python
Python3中的最大整数和最大浮点数实例
2019/07/09 Python
PyCharm 在Windows的有用快捷键详解
2020/04/07 Python
Django def clean()函数对表单中的数据进行验证操作
2020/07/09 Python
详解html5 shiv.js和respond.min.js
2018/01/24 HTML / CSS
激光脱毛、蓝光和护肤:Tria Beauty
2019/03/28 全球购物
您在慕尼黑的跑步商店:Lauf-bar
2019/10/11 全球购物
平面设计求职信
2014/03/10 职场文书
试用期辞职信范文
2015/03/02 职场文书
vue组件的路由高亮问题解决方法
2021/05/11 Vue.js