js实现观察者模式

  观察者模式:设计该模式背后的主要动力是促进形成松散耦合。在这种模式中,并不是一个对象调用另一个对象的方法,而是一个对象订阅另一个对象的特定活动并在状态改变后获得通知。订阅者也称为观察者,而补观察的对象称为发布者或主题。当发生了一个重要的事件时,发布者将会通知(调用)所有订阅者并且可能经常以事件对象的形式传递消息。

  思路:发布者对象需要一个数组类型的属性,以存储所有的订阅者。订阅(即注册)行为就是将新的订阅者加入到这个数组中去,则注销即是从这个数组中删除某个订阅者。此外,发布消息,就是循环遍历订阅者列表并通知他们。

  

  这里我的大体思路是对的,但是在发布者之外定义了一个新的类即订阅者。在订阅者中定义了一个方法getNews以便在发布者发布消息时调用该方法。然后面试官说这样太麻烦了,万一订阅者没有这个方法呢?然后我不是很懂……于是在发布消息时直接传递了参数:obj.news = msg; 然后面试官说这样不是更麻烦了吗?这样的话如果订阅者没有news这个属性怎么办?还得判断订阅者是否有news这个属性,没有的话就会出现undifined的报错。然后我就不知道该怎么做了……然后面试官为人特别nice,告诉我说可以用继承,或者是在注册时候就传入一个function。

 

下来后上网查相关,注意点如下:

  1. 发送消息即通知,意味着调用订阅者对象的某个方法。故当用户订阅信息时,该订阅者需要向paper的subscribe()提供它的其中一个方法。--------这应该就是面试官所说的注册时候就传入一个方法。

  2. 发布对象paper需要具有以下成员:

    a、 subscribers:一个数组,存储订阅者

    b、 subscribe():注册/订阅,将订阅者添加到subscribers数组中

    c、 unsubscribe(): 取消订阅。从subscribers数组中删除订阅者

    d、 publish() 循环遍历subscribers数组中的每一个元素,并且调用他们注册时所提供的方法

    所有这三种方法都需要一个type参数,因为发布者可能触发多个事件(比如同时发布一本杂志和一份报纸)而用户可能仅选择订阅其中一种,而不是另外一种。

  3、

 

 

参考《JavaScript模式》一书,使用字面量。实现代码如下:

 1         //由于这些成员对于任何发布者对象都是通用的,故将它们作为独立对象的一个部分来实现是很有意义的。那样我们可将其复制到任何对象中,并将任意给定对象变成一个发布者。
 2 
 3         //如下实现一个通用发布者
 4 
 5         //定义发布者对象...{}是定义一个对象
 6         var publisher = {
 7             subscribers: {
 8                 any: []         //event type: subscribers
 9             },
10             subscribe: function(fn,type){
11                 type = type || 'any';
12                 if(typeof this.subscribers[type] === "undefined"){
13                     this.subscribers[type] = [];
14                 }
15                 this.subscribers[type].push(fn);
16             },
17             unsubscribe: function(fn,type){
18                 this.visitSubscribers('unsubscribe', fn, type);
19             },
20             publish: function(publication, type){
21                 this.visitSubscribers('publish',publication,type);
22             },
23             visitSubscribers:function(action,arg,type){
24                 var pubtype = type ||'any',
25                     subscribers = this.subscribers[pubtype],
26                     i,
27                     max = subscribers.length;
28                 for(i=0;i<max;i++){
29                     if(action == "publish"){
30                         subscribers[i](arg);
31                     } else {
32                         if(subscribers[i] === arg){
33                             subscribers.splice(i,1);
34                         }
35                     }
36                 }
37             }
38         };
39         //定义一个函数makePublisher(),它接受一个对象作为对象,通过把上述通用发布者的方法复制到该对象中,从而将其转换为一个发布者
40         function makePublisher(o){
41             var i;
42             for(i in publisher) {
43                 if(publisher.hasOwnProperty(i) && typeof publisher[i] === "function"){
44                     o[i] = publisher[i];
45                 }
46             }
47             o.subscribers = {any: []};
48         }
49         //实现paper对象
50         var paper = {
51             daily: function(){
52                 this.publish("big news today");
53             },
54             monthly: function(){
55                 this.publish("interesting analysis","monthly");
56             }
57         };
58         //将paper构造成一个发布者
59         makePublisher(paper);
60         //已经有了一个发布者。看看订阅对象joe,该对象有两个方法:
61         var joe = {
62             drinkCoffee: function(paper) {
63                 console.log('Just read' + paper);
64             },
65             sundayPreNap : function(monthly){
66                 console.log('About to fall asleep reading this' + monthly);
67             }
68         };
69         //paper注册joe(即joe向paper订阅)
70         paper.subscribe(joe.drinkCoffee);
71         paper.subscribe(joe.sundayPreNap,'monthly');
72         //即joe为默认“any”事件提供了一个可被调用的方法,而另一个可被调用的方法则用于当“monthly”类型的事件发生时的情况。现在让我们来触发一些事件:
73         paper.daily();      //Just readbig news today
74         paper.daily();      //Just readbig news today
75         paper.monthly();    //About to fall asleep reading thisinteresting analysis
76         paper.monthly();    //About to fall asleep reading thisinteresting analysis
77         paper.monthly();

 

自己又试着实现了一下

 1     var publisher = {
 2         subscribers: {
 3             'any': []
 4         },
 5         subscribe: function(fn, type){
 6             var type = type || 'any';
 7             if(typeof this.subscribers[type] === 'undefined'){
 8                 this.subscribers[type] = [];
 9             }
10             this.subscribers[type].push(fn);
11         },
12         unsubscribe: function(fn, type){
13             this.visitSubscribers('unsubscribe', fn, type);
14         },
15         publish: function(publication, type){
16             this.visitSubscribers('publish', publication, type);
17         },
18         visitSubscribers: function(action, arg, type){
19             var type = type || 'any';
20             if(typeof this.subscribers[type] === 'undefined'){
21                 this.subscribers[type] = [];
22             }
23             for(var i=0, len = this.subscribers[type].length; i<len; i++){
24                 if(action === 'unsubscribe'){
25                     if(this.subscribers[type][i] === arg){
26                         this.subscribers[type].splice(i,1);
27                     }
28                 } else {
29                     this.subscribers[type][i](arg);
30                 }
31             }
32         }
33     };
34 
35     function makePublisher(obj){
36         var i;
37         for(i in publisher){
38             if(publisher.hasOwnProperty(i)  && typeof publisher[i] === 'function'){
39                 obj[i] = publisher[i];
40             }
41         }
42         obj['subscribers'] = {'any':[]};
43     }
44 
45     var paper = {
46         daily: function(news){
47             this.publish(news);
48         },
49         monthly : function(news){
50             this.publish(news, 'monthly');
51         }
52     }
53     makePublisher(paper);
54 
55     var Jack = {
56         drinkCoffee: function(publication){
57             console.log('Jack read '+ publication +' while drinking coffe');
58         },
59         watchTV : function(publication){
60             console.log('Jack read ' + publication + ' while watching TV');
61         }
62     }
63     var Amy = {
64         AmyDrinkCoffee: function(publication){
65             console.log('Amy read '+ publication +' while drinking coffe');
66         },
67         AmyWatchTV : function(publication){
68             console.log('Amy read ' + publication + ' while watching TV');
69         }
70     }
71 
72     paper.daily();
73     paper.monthly();
74     paper.subscribe(Jack.drinkCoffee);
75     paper.subscribe(Jack.watchTV, 'monthly');                                                           
76     paper.subscribe(Amy.AmyDrinkCoffee, 'monthly');
77     paper.subscribe(Amy.AmyWatchTV);
78 
79     console.log('paper发布daily');
80     paper.daily(' today is Sunday ');       //Jack read  today is Sunday  while drink coffe  
81                                             //Amy read  today is Sunday  while watch TV 
82     console.log('paper发布monthly');
83     paper.monthly(' this month is Aug. ');  //Jack read  this month is Aug.  while drink coffe
84                                             //Amy read  this month is Aug.  while watch TV
85     console.log('Amy取消了monthly的订阅');
86 
87     paper.unsubscribe(Amy.AmyWatchTV);
88     paper.daily(' today is Friday ');           //Jack read  today is Friday  while drinking coffe
89     paper.monthly(' next weekend is Sept. ');   //Jack read  next weekend is Sept.  while watching TV
90                                                 //Amy read  next weekend is Sept.  while drinking coffe
自己尝试实现

 

试着用函数实现:

 1         function Publisher(subscribers){
 2             this.subscribers = subscribers || {'any': []};
 3             Publisher.prototype.subscribe = function(fn, type){
 4                 var type = type || 'any';
 5                 if(typeof this.subscribers[type] === 'undefined'){
 6                     this.subscribers[type] = [];
 7                 }
 8                 this.subscribers[type].push(fn);
 9             };
10             Publisher.prototype.unsubscribe = function(fn, type){
11                 var type = type || 'any';
12                 for(var i=0, len = this.subscribers[type].length;  i<len; i++){
13                     if(this.subscribers[type][i] === fn){
14                         this.subscribers[type].splice(i,1);
15                     }
16                 }
17             };
18             Publisher.prototype.publish = function(publication, type){
19                 var type = type || 'any';
20                 for(var i=0, len = this.subscribers[type].length; i<len; i++){
21                     this.subscribers[type][i](publication);
22                 }
23             };
24         }
25 
26 
27         var paper = new Publisher();
28         paper.daily = function(){
29             this.publish(' this is Olympic ! ');
30         };
31         paper.monthly = function(){
32             this.publish(' last month is the 28th Olympic! ', 'monthly');
33         };
34 
35         var Jack = {
36             readInMorning: function(news){
37                 console.log('Jack reads ' + news + ' in the morning');
38             },
39             readInSunday: function(news){
40                 console.log('Jack reads ' + news + ' on Sunday');
41             }
42         };
43 
44         var Amy = {
45             readInMorning: function(news){
46                 console.log('Amy reads ' + news + ' in the morning');
47             },
48             readInSunday: function(news){
49                 console.log('Amy reads ' + news + ' on Sunday');
50             }
51         };
52 
53         paper.subscribe(Jack.readInMorning);
54         paper.subscribe(Jack.readInSunday, 'monthly');
55         paper.subscribe(Amy.readInMorning);
56         paper.subscribe(Amy.readInSunday, 'monthly');
57 
58         paper.daily();          //Jack reads  this is Olympic !  in the morning
59                                 //Amy reads  this is Olympic !  in the morning
60         paper.monthly();        //Jack reads  last month is the 28th Olympic!  on Sunday
61                                 //Amy reads  last month is the 28th Olympic!  on Sunday
62 
63         paper.unsubscribe(Jack.readInSunday,'monthly');
64         paper.daily();              //Jack reads  this is Olympic !  in the morning
65                                     //Amy reads  this is Olympic !  in the morning
66         paper.monthly();            //Amy reads  last month is the 28th Olympic!  on Sunday
67         

 

posted on 2016-08-11 16:39  杠子  阅读(13251)  评论(1编辑  收藏  举报

导航