Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

js设计模式--发布订阅模式 #34

Open
aermin opened this issue Mar 5, 2018 · 0 comments
Open

js设计模式--发布订阅模式 #34

aermin opened this issue Mar 5, 2018 · 0 comments

Comments

@aermin
Copy link
Owner

aermin commented Mar 5, 2018

定义:发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知。

现实中的发布订阅:小明最近看上了一套房子,到了售楼处之后才被告知,该楼盘的房子早已售罄。好在售楼 MM 告诉小明,不久后还有一些尾盘推出,开发商正在办理相关手续,手续办好后便可以购买。 但到底是什么时候,目前还没有人能够知道。运用发布订阅:小明离开之前,把电话号码留在 了售楼处。售楼 MM 答应他,新楼盘一推出就马上发信息通知小明。小红、小强和小龙也是一 样,他们的电话号码都被记在售楼处的花名册上,新楼盘推出的时候,售楼 MM 会翻开花名册, 遍历上面的电话号码,依次发送一条短信来通知他们。

发送短信通知就是一个典型的发布—订阅模式,小明、小红等购买者都是 订阅者,他们订阅了房子开售的消息。售楼处作为发布者,会在合适的时候遍历花名册上的电话 号码,依次给购房者发布消息。

好处:

购房者不用再天天给售楼处打电话咨询开售时间,在合适的时间点,售楼处作为发布者 会通知这些消息订阅者。说明发布—订阅模式可以广泛应用于异步编程中,这是一种替代传递回调函数的方案。

购房者和售楼处之间不再强耦合在一起,当有新的购房者出现时,他只需把手机号码留 在售楼处,售楼处不关心购房者的任何情况,不管购房者是男是女还是一只猴子。 而售 楼处的任何变动也不会影响购买者,比如售楼 MM 离职,售楼处从一楼搬到二楼,这些 改变都跟购房者无关,只要售楼处记得发短信这件事情。说明发布—订阅模式可以取代对象之间硬编码的通知机制,一个对象不用再显式地调 用另外一个对象的某个接口。

DOM 事件

只要我们曾经在 DOM 节点上面绑定过事件函数,那我们就曾经使用过发布—订阅 模式

document.body.addEventListener( 'click', function(){
     alert(2); 
}, false );
document.body.click();// 模拟用户点击

在这里需要监控用户点击 document.body 的动作,但是我们没办法预知用户将在什么时候点 击。所以我们订阅 document.body 上的 click 事件,当 body 节点被点击时,body 节点便会向订阅 者发布这个消息。

自定义事件

代码思路:如何一步步实现发布—订阅模式?

  • 首先要指定好谁充当发布者(比如售楼处);
  • 然后给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者(售楼处的花名册);
  • 最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函 数(遍历花名册,挨个发短信)。

代码

var Event = (function(){
    var clientList = {},/ /缓存列表,存放订阅者的回调函数
          listen, 
         trigger,
         remove;
    //订阅(消息)
    listen = function( key, fn ){ 
        if ( !this.clientList[ key ] ){  // 如果还没有订阅过此类消息,给该类消息创建一个缓存列表
            this.clientList[ key ] = []; 
        } 
        this.clientList[ key ].push( fn ); // 订阅的消息添加进消息缓存列表
     },  
    //发布(消息)
     trigger =  function(){  
        var key = Array.prototype.shift.call( arguments ),  // 取出消息类型
        fns = this.clientList[ key ];// 取出该消息对应的回调函数集合
        if ( !fns || fns.length === 0 ){  // 如果没有订阅该消息,则返回
            return false; 
        }
        for( var i = 0, fn; fn = fns[ i++ ]; ){ 
            fn.apply( this, arguments ); // (2) // arguments 是 trigger 时带上的参数 
        }
     }
   //取消订阅事件
   remove = function( key, fn ){
     var fns = clientList[ key ]; 
     if ( !fns ){ return false; } // 如果 key 对应的消息没有被人订阅,则直接返回
     if ( !fn ){ //如果没有传入具体的回调函数,表示需要取消 key 对应消息的所有订阅.也就是取消全部订阅
         fns && ( fns.length = 0 ); 
     }else{   //取消具体的订阅
          for ( var l = fns.length - 1; l >=0; l-- ){  // 反向遍历订阅的回调函数列表
               var _fn = fns[ l ]; 
               if ( _fn === fn ){  // 删除订阅者的回调函数
                    fns.splice( l, 1 );  
               } 
          } 
     }
  };
  return { 
     listen: listen, 
     trigger: trigger, 
     remove: remove 
  }
})();

测试

Event.listen( 'squareMeter88', function( price ){ // 小红订阅消息
    console.log( '价格= ' + price ); // 输出:'价格=2000000' 
});
Event.trigger( 'squareMeter88', 2000000 );// 售楼处发布消息
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant