日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

用 JavaScript 实现发布/订阅模式

發(fā)布時間:2024/5/8 javascript 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用 JavaScript 实现发布/订阅模式 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

重要申明:感謝原文作者——杠子,想看原文請戳這里!我轉(zhuǎn)載了原文,修改了一些原文行文不通暢之處,并更改附上了自己的代碼段,望請勿怪。掛在自己的博客下面一方面是為了收藏好文,方便自己溫顧而知新;另一方面也希望更多人看到這文章。PS:原文撰寫于 2016-08-11。

一、發(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)知識,整理出的注意點(diǎ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ù)。這是因?yàn)榘l(fā)布者可能觸發(fā)多個事件*(比如同時發(fā)布一本雜志和一份報紙)*,而訂閱者可能僅選擇訂閱其中一種,而另外一種不訂閱。
  • 三、代碼實(shí)現(xiàn)

    參考《 JavaScript 模式》一書,使用字面量實(shí)現(xiàn)代碼如下:

    // 由于這些成員對于任何發(fā)布者對象都是通用的,故將它們作為獨(dú)立對象的一個部分來實(shí)現(xiàn)是很有意義的。那樣我們可將其復(fù)制到任何對象中,并將任意給定對象變成一個發(fā)布者。 // 如下實(shí)現(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: [] }; }// 實(shí)現(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ā)生時的情況。現(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ù)自己又實(shí)現(xiàn)了一遍)*:

    // 由于這些成員對于任何發(fā)布者對象都是通用的,故將它們作為獨(dú)立對象的一個部分來實(shí)現(xiàn)是很有意義的。那樣我們可將其復(fù)制到任何對象中,并將任意給定對象變成一個發(fā)布者。 // 如下實(shí)現(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(item);}});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();} }// 實(shí)現(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ā)生時的情況。現(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!

    總結(jié)

    以上是生活随笔為你收集整理的用 JavaScript 实现发布/订阅模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。