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         

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM