学习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 相关文章推荐
如何快速的呈现我们的网页的技巧整理
Jul 01 Javascript
Jquery幻灯片特效代码分享--鼠标点击按钮时切换(1)
Aug 15 Javascript
js读取并解析JSON类型数据的方法
Nov 14 Javascript
javascript日期比较方法实例分析
Jun 17 Javascript
微信小程序 vidao实现视频播放和弹幕的功能
Nov 02 Javascript
JS IOS/iPhone的Safari浏览器不兼容Javascript中的Date()问题如何解决
Nov 11 Javascript
jquery 手势密码插件
Mar 17 Javascript
babel之配置文件.babelrc入门详解
Feb 22 Javascript
Angular 多模块项目构建过程
Feb 13 Javascript
Bootstrap实现前端登录页面带验证码功能完整示例
Mar 26 Javascript
JavaScript eval()函数定义及使用方法详解
Jul 07 Javascript
JS代码简洁方式之函数方法详解
Jul 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
德劲1107的电路分析与打磨
2021/03/02 无线电
第七节--类的静态成员
2006/11/16 PHP
PHP中is_file不能替代file_exists的理由
2014/03/04 PHP
php+ajax实时输入自动搜索匹配的方法
2014/12/26 PHP
PHP pthreads v3在centos7平台下的安装与配置操作方法
2020/02/21 PHP
node.js中的buffer.Buffer.isBuffer方法使用说明
2014/12/14 Javascript
JavaScript中的this到底是什么(一)
2015/12/09 Javascript
15个常用的jquery代码片段
2015/12/19 Javascript
微信小程序之picker日期和时间选择器
2017/02/09 Javascript
Vue组件和Route的生命周期实例详解
2018/02/10 Javascript
vue input 输入校验字母数字组合且长度小于30的实现代码
2018/05/16 Javascript
vue-router中scrollBehavior的巧妙用法
2018/07/09 Javascript
vue实现文件上传功能
2018/08/13 Javascript
js for终止循环 跳出多层循环
2018/10/04 Javascript
小程序二次贝塞尔曲线实现购物车商品曲线飞入效果
2019/01/07 Javascript
JavaScript实现星级评价效果
2019/05/17 Javascript
jquery操作select常见方法大全【7种情况】
2019/05/28 jQuery
vue+elementUI实现表格关键字筛选高亮
2020/10/26 Javascript
vue给对象动态添加属性和值的实例
2019/09/09 Javascript
vue设置默认首页的操作
2020/08/12 Javascript
[17:45]DOTA2 HEROES教学视频教你分分钟做大人-军团指挥官
2014/06/11 DOTA
盘点提高 Python 代码效率的方法
2014/07/03 Python
python在不同层级目录import模块的方法
2016/01/31 Python
浅谈tensorflow中几个随机函数的用法
2018/07/27 Python
python读取图片任意范围区域
2019/01/23 Python
python输出第n个默尼森数的实现示例
2020/03/08 Python
详解Python yaml模块
2020/09/23 Python
HTML5 Web存储方式的localStorage和sessionStorage进行数据本地存储案例应用
2012/12/09 HTML / CSS
通信工程毕业生求职信
2013/11/16 职场文书
国贸专业自荐信范文
2014/03/02 职场文书
司法建议书范文
2014/05/13 职场文书
领导干部群众路线剖析材料
2014/10/09 职场文书
领导干部作风整顿个人剖析材料
2014/10/11 职场文书
致青春观后感
2015/06/09 职场文书
运动会报道稿大全
2015/07/23 职场文书
你真的了解PHP中的引用符号(&)吗
2021/05/12 PHP