觀察者模式:設計該模式背後的主要動力是促進造成鬆散耦合。在這種模式中,並非一個對象調用另外一個對象的方法,而是一個對象訂閱另外一個對象的特定活動並在狀態改變後得到通知。訂閱者也稱爲觀察者,而補觀察的對象稱爲發佈者或主題。當發生了一個重要的事件時,發佈者將會通知(調用)全部訂閱者而且可能常常以事件對象的形式傳遞消息。面試
思路:發佈者對象須要一個數組類型的屬性,以存儲全部的訂閱者。訂閱(即註冊)行爲就是將新的訂閱者加入到這個數組中去,則註銷便是從這個數組中刪除某個訂閱者。此外,發佈消息,就是循環遍歷訂閱者列表並通知他們。數組
這裏個人大致思路是對的,可是在發佈者以外定義了一個新的類即訂閱者。在訂閱者中定義了一個方法getNews以便在發佈者發佈消息時調用該方法。而後面試官說這樣太麻煩了,萬一訂閱者沒有這個方法呢?而後我不是很懂……因而在發佈消息時直接傳遞了參數:obj.news = msg; 而後面試官說這樣不是更麻煩了嗎?這樣的話若是訂閱者沒有news這個屬性怎麼辦?還得判斷訂閱者是否有news這個屬性,沒有的話就會出現undifined的報錯。而後我就不知道該怎麼作了……而後面試官爲人特別nice,告訴我說能夠用繼承,或者是在註冊時候就傳入一個function。ide
下來後上網查相關,注意點以下:函數
1. 發送消息即通知,意味着調用訂閱者對象的某個方法。故當用戶訂閱信息時,該訂閱者須要向paper的subscribe()提供它的其中一個方法。--------這應該就是面試官所說的註冊時候就傳入一個方法。this
2. 發佈對象paper須要具備如下成員:spa
a、 subscribers:一個數組,存儲訂閱者prototype
b、 subscribe():註冊/訂閱,將訂閱者添加到subscribers數組中設計
c、 unsubscribe(): 取消訂閱。從subscribers數組中刪除訂閱者rest
d、 publish() 循環遍歷subscribers數組中的每個元素,而且調用他們註冊時所提供的方法code
全部這三種方法都須要一個type參數,由於發佈者可能觸發多個事件(好比同時發佈一本雜誌和一份報紙)而用戶可能僅選擇訂閱其中一種,而不是另一種。
三、
參考《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