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();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容