Javascript实现简单地发布订阅模式

不论是在程序世界里还是现实生活中,发布—订阅模式的应用都非常广泛。我们先看一下现实中的例子。 
小明最近看上了一套房子,到了售楼处之后才被告知,该楼盘的房子早已售罄。好在售楼MM告诉小明,不久后还有一些尾盘推出。开发商正在办理相关手续,手续办好后便可以购买。但到底是什么时候,目前还没有人能够知道。 
于是小明记下了售楼处的电话,以后每天都会打电话过去询问是不是已经到了购买时间。除了小明,还有小红,小强,小龙也会每天向售楼处咨询这个问题。一个星期过后,售楼MM决定辞职,因为厌倦了每天回答1000个相同的电话。 
当然现实中没有这么笨的销售公司,实际上故事是这样的:小明离开之前,把电话号码留在售楼处。售楼MM答应他,新楼盘一推出就马上发信息通知小明。小红、小强、小龙也是一样,他们的电话号码都被记在售楼处的花名册上,新楼盘推出的时候,售楼MM会翻开花名册,遍历上面的电话号码,依次发送一条短信通知他们。

 

发布订阅模式的优点

 

    1. 可以广泛应用于异步编程,它可以代替我们传统的回调函数,我们不需要关注对象在异步执行阶段的内部状态,我们只关心事件完成的时间点。

    2. 取代对象之间硬编码通知机制,一个对象不必显式调用另一个对象的接口,而是松耦合的联系在一起 
      虽然不知道彼此的细节,但不影响相互通信。更重要的是,其中一个对象改变不会影响另一个对象。

我们实现一个简单地发布订阅模型:

 

 1 // 首先定义消息的发布者
 2 const  salesoffice = {};
 3 
 4 // 定义缓存列表,存放订阅者的回调函数列表;
 5 salesoffice.clientList =[];
 6 
 7 // 设置订阅者
 8 
 9 salesoffice.listen = function(key, fn){
10     if(!this.clientList[key]) {
11         this.clientList[key] = [];
12     }
13     this.clientList[key].push(fn);
14 }
15 
16 // 设置发布事件
17 salesoffice.trigger = function(){
18     var key = Array.prototype.shift.call(arguments),
19               fns = this.clientList[key];
20     if(!fns  || fns.length === 0) {
21         return false;
22     }
23     for(var i = 0 , fn ; fn = fns[i++];) {
24         fn.apply(this, arguments);
25     }
26 }
27 
28 //示例
29 
30 salesoffice.listen('aaaaa' , function(a, b){
31    console.log(a);
32    console.log( b);
33 })
34 
35 
36  salesoffice.listen('abcde' , function(a, b){
37     console.log(a);
38     console.log(b);
39     console.log(a+b);
40 }) 
41 
42 console.log(salesoffice);
43 
44 salesoffice.trigger('aaaaa' , 100 , 200);            //  100  200  300
45 
46 salesoffice.trigger('abcde', 111 , 222);            //  111 222 333

 

一个通用的发布订阅模型和事件

 1 // 定义发布/订阅模型
 2 const event = {
 3     // 设置缓存列表,存放订阅者的回调函数列表
 4     list : [],
 5     // 设置订阅者
 6     listen :  function(key , fn){
 7         if(!this.list[key]){
 8             this.list[key] = [];
 9         }
10         // 将订阅的消息添加到缓存列表中
11         this.list[key].push(fn);
12     },
13 
14     // 发布事件
15     trigger : function() {
16         const key = Array.prototype.shift.call(arguments),
17                   fns = this.list[key];
18         if(!fns ||  fns.length === 0){
19              return false;
20         }
21         for(let i = 0 , fn ; fn = fns[i++];){
22              fn.apply(this, arguments);
23         }
24     },
25 
26     // 取消订阅
27     remove : function(key , fn){
28         const fns = this.list[key];
29         // 如果key对应的消息没有订阅过的话,则返回
30         if(!fns) {
31             return false;
32         }
33         // 如果没有传入具体的回调函数,表示需要取消key对应消息的所有订阅
34         if(!fn) {
35             delete this.list[key]; //如果没有后续参数,则删除整个回调数组
36         }else {
37             for(let i = fns.length - 1 ; i>=0 ;i--) {
38                 const _fn = fns[i];
39                 if(_fn === fn) {
40                     fns.splice( i, 1);   // 删除订阅者的回调函数
41                 }
42             }    
43         }
44     }
45 };
46 
47 const initEvent = function(obj) {
48     for(let i in event) {
49         obj[i] = event[i];
50     }
51 };
52 
53 const shoeobj = {};
54 
55 initEvent(shoeobj);
56 
57 shoeobj.listen('abcd' , function(a, b) {
58     console.log(a);
59     console.log(b);
60 })
61 
62 shoeobj.listen('efgh' , function(a, b){
63     console.log(a);
64     console.log(b);
65 })
66 
67 shoeobj.trigger('abcd' , 100 ,200);             //   100  200
68  
69 shoeobj.trigger('efgh' ,  300, 500);           //  300  500
70 
71 shoeobj.remove('abcd');
72 
73 shoeobj.trigger('abcd', 20, 50);              //  false

 

posted @ 2018-05-02 21:41  曼施坦因  阅读(1540)  评论(0编辑  收藏  举报