JavaScript状态机

状态机——之前我们称之为“有限状态机”(Finite State Machines,FSM)使用状态机可以轻松地管理很多控制器,根据需要显示和隐藏视图。那么,到底什么是状态机?本质上讲状态机由两部分组成:状态和转换器。它只有一个活动状态,但也包含很多非活动状态(passive state)。当活动状态之间相互切换时就会调用状态转换器。状态机如何工作呢?考虑这样一个场景,应用中存在一些视图,它们的显示是相互独立的,比如一个视图用来显示联系人,另一个视图用来编辑联系人。这两个视图一定是互斥的关系,其中一个显示时另一个一定是隐藏的。这个场景就非常适合引入状态机,因为它能确保每个时刻只有一种视图是激活的。的确,如果我们想添加一些新视图,比如一个承载设置操作的视图,用状态机来处理这种场景绰绰有余。我们来对实际的例子做进一步修改,来看一看实现一个状态机的思路是怎样的。这个例子非常简单,没有实现多个转换器类型,但足以满足我们的需要。首先,我们使用jQuery 的事件API 创建一个Events 对象,给它添加绑定和触发状态机的
事件的能力:

var Events = {
     bind: function(){
if ( !this.o ) this.o = $({});
this.o.bind.apply(this.o, arguments);
     },
     trigger: function(){
if ( !this.o ) this.o = $({});
this.o.trigger.apply(this.o, arguments);
     }
};

这里的Events 对象本质上是扩展了jQuery 现有的DOM 外部事件的支持,这样我们就可以将它应用到我们的库中。现在我们来创建StateMachine 类,它包含一个主要的函数add() :

var StateMachine = function(){};
StateMachine.fn = StateMachine.prototype;

// 添加事件绑定或触发行为
  $.extend(StateMachine.fn, Events);
   StateMachine.fn.add = function(controller){
 this.bind("change", function(e, current){ //this指向StateMachine的实例,给其绑定change事件
        if (controller == current)
        controller.activate();
         else
         controller.deactivate();
   });
  controller.active = $.proxy(function(){   //这个controller.active为传进来的参数中的active对象,触发提供了设置。例如con1中的active对象 绑定到了sateMachine的原型上,实例化即被触发。
        this.trigger("change", controller);
   }, this);
 };

这个状态机的add() 函数将传入的控制器添加至状态列表,并创建一个active() 函数。当调用active() 的时候,控制器的状态就转换为激活状态。对于激活状态的控制器,状态机将基于它调用activate(),对于其他的控制器,状态机则会调用deactivate()。这里给出两个例子,通过例子可以看出这两类控制器是如何工作的,我们首先将控制器添加至状态机中,然后激活其中一个控制器:

var con1 = {
     activate: function(){ /* ... */ },
     deactivate: function(){ /* ... */ }
};
var con2 = {
     activate: function(){ /* ... */ },
     deactivate: function(){ /* ... */ }
};
// 创建一个新的状态机,并添加状态
var sm = new StateMachine;
sm.add(con1);
sm.add(con2);
// 激活第1 个状态
con1.active();

状态机的add() 函数给change 事件创建了一个回调,根据需要调用activate() 或deactivate() 函数。尽管状态机给我们提供了active() 函数,我们同样可以通过手动触发change 事件来改变状态:

sm.trigger("change", con2);

在控制器activate() 函数的内部,我们可以创建并显示它的视图,添加并显示元素。与此类似,在deactivate() 函数内部我们则将元素销毁来隐藏视图。可以通过CSS 的类来隐藏和显示视图,这种方法非常不错,即给元素添加名为.active 的类来显示视图,将它移除就可以隐藏视图:

var con1 = {
activate: function(){
     $("#con1").addClass("active");
},
deactivate: function(){
    $("#con1").removeClass("active");
   }
};
var con2 = {
activate: function(){
    $("#con2").addClass("active");
},
 deactivate: function(){
      $("#con2").removeClass("active");
    }
};
然后在样式表中保证视图包含.active 类,否则不包含.active 类:
```javascript
#con1, #con2 { display: none; }
#con1.active, #con2.active { display: block; }

完整代码

   var Events = {
      bind: function(){
        if ( !this.o ) this.o = $({});
        this.o.bind.apply(this.o, arguments);
      },
     
      trigger: function(){
        if ( !this.o ) this.o = $({});
        this.o.trigger.apply(this.o, arguments);
      }
    };
   
    var StateMachine = function(){};
    StateMachine.fn  = StateMachine.prototype;
    $.extend(StateMachine.fn, Events);
  
    StateMachine.fn.add = function(controller){
      this.bind("change", function(e, current){
        if (controller == current)
          controller.activate();
        else
          controller.deactivate();
      });
     
      controller.active = $.proxy(function(){
        this.trigger("change", controller);
      }, this);
    };
   
    var con1 = {
      activate: function(){
        console.log("controller 1 activated");
      },
      deactivate: function(){
        console.log("controller 1 deactivated");
      }
    };
   
    var con2 = {
      activate: function(){
        console.log("controller 2 activated");
      },
      deactivate: function(){
        console.log("controller 2 deactivated");
      }
    };
   
    var sm = new StateMachine;
    sm.add(con1);
    sm.add(con2);    
    con1.active();
    原文作者:MakingChoice
    原文地址: https://www.jianshu.com/p/bf99dc4c0abb
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞