JavaScript實現(xiàn)與使用發(fā)布/訂閱模式詳解
本文實例講述了JavaScript實現(xiàn)與使用發(fā)布/訂閱模式。分享給大家供大家參考,具體如下:
一、發(fā)布/訂閱模式簡介
發(fā)布/訂閱模式(即觀察者模式): 設(shè)計該模式背后的主要動力是促進(jìn)形成松散耦合。在這種模式中,并不是一個對象調(diào)用另一個對象的方法,而是一個訂閱者對象訂閱發(fā)布者對象的特定活動,并在發(fā)布者對象的狀態(tài)發(fā)生改變后,訂閱者對象獲得通知。訂閱者也稱為觀察者,而被觀察的對象稱為發(fā)布者或主題。當(dāng)發(fā)生了一個重要的事件時,發(fā)布者將會通知(調(diào)用)所有訂閱者,并且可能經(jīng)常以事件對象的形式傳遞消息。
基本思路:發(fā)布者對象需要一個數(shù)組類型的屬性,以存儲所有的訂閱者。訂閱(即注冊)就是將新的訂閱者加入到這個數(shù)組中去,而注銷即是從這個數(shù)組中刪除某個訂閱者。此外,發(fā)布消息就是循環(huán)遍歷訂閱者列表并通知他們。
二、如何發(fā)布訂閱者的方法?
這里我的大體思路是對的。不過當(dāng)時面試時,我還說了“在發(fā)布者之外,還需要定義了一個新的類——訂閱者。在訂閱者中,需要定義了一個類似 getNews 的方法,以便在發(fā)布者發(fā)布消息時,調(diào)用該方法”。然后,面試官說這樣太麻煩了,萬一訂閱者沒有這個方法呢?然后,我不是很懂……
于是我就想到了,在發(fā)布消息時直接傳遞了參數(shù):obj.news = msg; 然后面試官說這樣不是更麻煩了嗎?這樣的話,如果訂閱者沒有 news 這個屬性怎么辦?還得判斷訂閱者是否有 news 這個屬性,沒有的話就會出現(xiàn) undifined 的報錯。
然后,我就不知道該怎么做了……然后面試官為人特別 nice ,告訴我“可以用繼承或者是在注冊時候就傳入一個 function ”。
面試完后,回家上網(wǎng)查相關(guān)知識,整理出的注意點如下:
- 發(fā)送消息,即通知,意味著調(diào)用訂閱者對象的某個方法。故當(dāng)用戶訂閱信息時,該訂閱者需要向發(fā)布者的 subscribe() 提供它的其中一個方法——這應(yīng)該就是面試官所說的注冊時候就傳入一個方法。
- 每個發(fā)布者對象需要具有以下成員:
subscribers:一個數(shù)組,存儲訂閱者;
subscribe():注冊/訂閱,將訂閱者添加到 subscribers 數(shù)組中;
unsubscribe():取消訂閱。從 subscribers 數(shù)組中刪除訂閱者;
publish():循環(huán)遍歷 subscribers 數(shù)組中的每一個元素,并且調(diào)用它們注冊時所提供的方法;
所有這三種方法都需要一個 type 參數(shù)。這是因為發(fā)布者可能觸發(fā)多個事件(比如同時發(fā)布一本雜志和一份報紙),而訂閱者可能僅選擇訂閱其中一種,而另外一種不訂閱。
三、代碼實現(xiàn)
參考《 JavaScript 模式》一書,使用字面量實現(xiàn)代碼如下:
// 由于這些成員對于任何發(fā)布者對象都是通用的,故將它們作為獨立對象的一個部分來實現(xiàn)是很有意義的。那樣我們可將其復(fù)制到任何對象中,并將任意給定對象變成一個發(fā)布者。
// 如下實現(xiàn)一個通用發(fā)布者,定義發(fā)布者對象……
let publisher = {
subscribers: {
any: []
},
subscribe: function (fn, type = `any`) {
if (typeof this.subscribers[type] === `undefined`) {
this.subscribers[type] = [];
}
this.subscribers[type].push(fn);
},
unSubscribe: function (fn, type = `any`) {
let newSubscribers = [];
this.subscribers[type].forEach((item, i) => {
if (item !== fn) {
newSubscribers.push(fn);
}
});
this.subscribers[type] = newSubscribers;
},
publish: function (args, type = `any`) {
this.subscribers[type].forEach((item, i) => {
item(args);
});
}
};
// 定義一個函數(shù)makePublisher(),它接受一個對象作為參數(shù),通過把上述通用發(fā)布者的方法復(fù)制到該對象中,從而將其轉(zhuǎn)換為一個發(fā)布者
function makePublisher(obj) {
for (let i in publisher) {
if (publisher.hasOwnProperty(i) && typeof publisher[i] === `function`) {
obj[i] = publisher[i];
}
}
obj.subscribers = { any: [] };
}
// 實現(xiàn)paper對象
var paper = {
daily: function () {
this.publish(`big news today!`);
},
monthly: function () {
this.publish(`interesting analysis`, `monthly`);
}
};
// 將paper構(gòu)造成一個發(fā)布者
makePublisher(paper);
// 看看訂閱對象joe,該對象有兩個方法:
var joe = {
drinkCoffee: function (paper) {
console.log(`Just read ` + paper);
},
sundayPreNap: function (monthly) {
console.log(`About to fall asleep reading this ` + monthly);
}
};
// paper注冊joe(即joe向paper訂閱)
paper.subscribe(joe.drinkCoffee);
paper.subscribe(joe.sundayPreNap, `monthly`);
// 即joe為默認(rèn)“any”事件提供了一個可被調(diào)用的方法,而另一個可被調(diào)用的方法則用于當(dāng)“monthly”類型的事件發(fā)生時的情況?,F(xiàn)在讓我們來觸發(fā)一些事件:
paper.daily(); // Just read big news today
paper.daily(); // Just read big news today
paper.monthly(); // About to fall asleep reading this interesting analysis
paper.monthly(); // About to fall asleep reading this interesting analysis
paper.monthly(); // About to fall asleep reading this interesting analysis
我自己又嘗試用 ES6 的 class 語法寫了一遍(PS:這是轉(zhuǎn)載者自己寫的,原文作者是用函數(shù)自己又實現(xiàn)了一遍):
// 由于這些成員對于任何發(fā)布者對象都是通用的,故將它們作為獨立對象的一個部分來實現(xiàn)是很有意義的。那樣我們可將其復(fù)制到任何對象中,并將任意給定對象變成一個發(fā)布者。
// 如下實現(xiàn)一個通用發(fā)布者,定義發(fā)布者對象……
class Publisher {
constructor(){
this.subscribers = {
any: []
}
}
subscribe(fn, type=`any`){
if(typeof this.subscribers[type] === `undefined`){
this.subscribers[type] = [];
}
this.subscribers[type].push(fn);
}
unSubscribe(fn, type=`any`){
let newArr = [];
this.subscribers[type].forEach((item, i) => {
if(item !== fn){
newArr.push(fn);
}
});
this.subscribers[type] = newArr;
}
publish(args, type=`any`){
this.subscribers[type].forEach((item, i) => {
item(args);
});
}
// 定義一個函數(shù)makePublisher(),它接受一個對象作為參數(shù),通過把上述通用發(fā)布者的方法復(fù)制到該對象中,從而將其轉(zhuǎn)換為一個發(fā)布者
static makePublisher(obj){
obj.publisher = new Publisher();
}
}
// 實現(xiàn)person對象
var person = {
sayHi: function(name){
this.publisher.publish(name);
},
sayAge: function(num){
this.publisher.publish(num, `age`);
}
}
// 將person構(gòu)造成一個發(fā)布者
Publisher.makePublisher(person);
// 看看訂閱對象myLover,該對象有兩個方法:
var myLover = {
name: ``,
hello: function(name){
this.name = name;
console.log(`Hi, i am ` + name + ` ! Nice to meet you!`);
},
timeOfLife: function(num){
console.log(`Hello! My name is ` + this.name + ` ! I am ` + num + ` years old!`);
}
}
// person注冊myLover(即myLover向person訂閱)
person.publisher.subscribe(myLover.hello);
person.publisher.subscribe(myLover.timeOfLife, `age`);
// 即myLover為默認(rèn)“any”事件提供了一個可被調(diào)用的方法,而另一個可被調(diào)用的方法則用于當(dāng)“age”類型的事件發(fā)生時的情況?,F(xiàn)在讓我們來觸發(fā)一些事件:
person.sayHi(`Jimmy`); // Hi, i am Jimmy ! Nice to meet you!
person.sayAge(24); // Hello! My name is Jimmy ! I am 24 years old!
person.sayHi(`Tom`); // Hi, i am Tom ! Nice to meet you!
person.sayAge(6); // Hello! My name is Tom ! I am 6 years old!
person.sayAge(18); // Hello! My name is Tom ! I am 18 years old!
更多關(guān)于JavaScript相關(guān)內(nèi)容還可查看本站專題:《javascript面向?qū)ο笕腴T教程》、《JavaScript錯誤與調(diào)試技巧總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》、《JavaScript遍歷算法與技巧總結(jié)》及《JavaScript數(shù)學(xué)運算用法總結(jié)》
希望本文所述對大家JavaScript程序設(shè)計有所幫助。
- JavaScript事件發(fā)布/訂閱模式原理與用法分析
- JavaScript中發(fā)布/訂閱模式的簡單實例
- JS前端設(shè)計模式之發(fā)布訂閱模式詳解
- js 發(fā)布訂閱模式的實例講解
- JavaScript設(shè)計模式之觀察者模式(發(fā)布訂閱模式)原理與實現(xiàn)方法示例
- JavaScript設(shè)計模式之觀察者模式與發(fā)布訂閱模式詳解
- 詳解JavaScript設(shè)計模式中的享元模式
- JavaScript設(shè)計模式之單例模式應(yīng)用場景案例詳解
- JavaScript 設(shè)計模式 安全沙箱模式
- JavaScript設(shè)計模式之觀察者模式(發(fā)布者-訂閱者模式)
- javascript 發(fā)布-訂閱模式 實例詳解
相關(guān)文章
jQuery與JavaScript節(jié)點創(chuàng)建方法的對比
本文主要介紹jQuery與JavaScript節(jié)點的創(chuàng)建方法,以及他們的具體代碼實現(xiàn)方法,大家可以對比下他們之間的不同,希望對大家編寫代碼有所幫助2016-11-11
JS實現(xiàn)線性表的順序表示方法示例【經(jīng)典數(shù)據(jù)結(jié)構(gòu)】
這篇文章主要介紹了JS實現(xiàn)線性表的順序表示方法,簡單分析了線性表的原理并結(jié)合實例形式給出了線性表的插入與刪除實現(xiàn)技巧,需要的朋友可以參考下2017-04-04
Webpack中SplitChunksPlugin 配置參數(shù)詳解
這篇文章主要介紹了Webpack中SplitChunksPlugin 配置參數(shù)詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
javascript小數(shù)計算出現(xiàn)近似值的解決辦法
在javascript里面,小數(shù)只能進(jìn)行相似計算,例如:5.06+1.30,你得到的結(jié)果會是6.359999999999999,但有的小數(shù)計算又是正確的,如果計算出現(xiàn)了近似值,你可以用如下的方法計算。2010-02-02

