中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JavaScript實現與使用發布/訂閱模式詳解

發布時間:2020-09-04 18:50:13 來源:腳本之家 閱讀:112 作者:沐風的心 欄目:web開發

本文實例講述了JavaScript實現與使用發布/訂閱模式。分享給大家供大家參考,具體如下:

一、發布/訂閱模式簡介

發布/訂閱模式(即觀察者模式): 設計該模式背后的主要動力是促進形成松散耦合。在這種模式中,并不是一個對象調用另一個對象的方法,而是一個訂閱者對象訂閱發布者對象的特定活動,并在發布者對象的狀態發生改變后,訂閱者對象獲得通知。訂閱者也稱為觀察者,而被觀察的對象稱為發布者或主題。當發生了一個重要的事件時,發布者將會通知(調用)所有訂閱者,并且可能經常以事件對象的形式傳遞消息。

基本思路:發布者對象需要一個數組類型的屬性,以存儲所有的訂閱者。訂閱(即注冊)就是將新的訂閱者加入到這個數組中去,而注銷即是從這個數組中刪除某個訂閱者。此外,發布消息就是循環遍歷訂閱者列表并通知他們。

二、如何發布訂閱者的方法?

這里我的大體思路是對的。不過當時面試時,我還說了“在發布者之外,還需要定義了一個新的類——訂閱者。在訂閱者中,需要定義了一個類似 getNews 的方法,以便在發布者發布消息時,調用該方法”。然后,面試官說這樣太麻煩了,萬一訂閱者沒有這個方法呢?然后,我不是很懂……

于是我就想到了,在發布消息時直接傳遞了參數:obj.news = msg; 然后面試官說這樣不是更麻煩了嗎?這樣的話,如果訂閱者沒有 news 這個屬性怎么辦?還得判斷訂閱者是否有 news 這個屬性,沒有的話就會出現 undifined 的報錯。

然后,我就不知道該怎么做了……然后面試官為人特別 nice ,告訴我“可以用繼承或者是在注冊時候就傳入一個 function ”。

面試完后,回家上網查相關知識,整理出的注意點如下:

  1. 發送消息,即通知,意味著調用訂閱者對象的某個方法。故當用戶訂閱信息時,該訂閱者需要向發布者的 subscribe() 提供它的其中一個方法——這應該就是面試官所說的注冊時候就傳入一個方法。
  2. 每個發布者對象需要具有以下成員:

subscribers:一個數組,存儲訂閱者;
subscribe():注冊/訂閱,將訂閱者添加到 subscribers 數組中;
unsubscribe():取消訂閱。從 subscribers 數組中刪除訂閱者;
publish():循環遍歷 subscribers 數組中的每一個元素,并且調用它們注冊時所提供的方法;
所有這三種方法都需要一個 type 參數。這是因為發布者可能觸發多個事件(比如同時發布一本雜志和一份報紙),而訂閱者可能僅選擇訂閱其中一種,而另外一種不訂閱。

三、代碼實現

參考《 JavaScript 模式》一書,使用字面量實現代碼如下:

// 由于這些成員對于任何發布者對象都是通用的,故將它們作為獨立對象的一個部分來實現是很有意義的。那樣我們可將其復制到任何對象中,并將任意給定對象變成一個發布者。
// 如下實現一個通用發布者,定義發布者對象……
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);
  });
 }
};
// 定義一個函數makePublisher(),它接受一個對象作為參數,通過把上述通用發布者的方法復制到該對象中,從而將其轉換為一個發布者
function makePublisher(obj) {
 for (let i in publisher) {
  if (publisher.hasOwnProperty(i) && typeof publisher[i] === `function`) {
   obj[i] = publisher[i];
  }
 }
 obj.subscribers = { any: [] };
}
// 實現paper對象
var paper = {
 daily: function () {
  this.publish(`big news today!`);
 },
 monthly: function () {
  this.publish(`interesting analysis`, `monthly`);
 }
};
// 將paper構造成一個發布者
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為默認“any”事件提供了一個可被調用的方法,而另一個可被調用的方法則用于當“monthly”類型的事件發生時的情況。現在讓我們來觸發一些事件:
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:這是轉載者自己寫的,原文作者是用函數自己又實現了一遍):

// 由于這些成員對于任何發布者對象都是通用的,故將它們作為獨立對象的一個部分來實現是很有意義的。那樣我們可將其復制到任何對象中,并將任意給定對象變成一個發布者。
// 如下實現一個通用發布者,定義發布者對象……
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);
  });
 }
 // 定義一個函數makePublisher(),它接受一個對象作為參數,通過把上述通用發布者的方法復制到該對象中,從而將其轉換為一個發布者
 static makePublisher(obj){
  obj.publisher = new Publisher();
 }
}
// 實現person對象
var person = {
 sayHi: function(name){
  this.publisher.publish(name);
 },
 sayAge: function(num){
  this.publisher.publish(num, `age`);
 }
}
// 將person構造成一個發布者
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為默認“any”事件提供了一個可被調用的方法,而另一個可被調用的方法則用于當“age”類型的事件發生時的情況。現在讓我們來觸發一些事件:
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!

更多關于JavaScript相關內容還可查看本站專題:《javascript面向對象入門教程》、《JavaScript錯誤與調試技巧總結》、《JavaScript數據結構與算法技巧總結》、《JavaScript遍歷算法與技巧總結》及《JavaScript數學運算用法總結》

希望本文所述對大家JavaScript程序設計有所幫助。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

永登县| 罗平县| 义马市| 留坝县| 西城区| 镇江市| 长宁区| 柳河县| 游戏| 麻栗坡县| 克什克腾旗| 连州市| 汾阳市| 咸阳市| 会昌县| 八宿县| 克什克腾旗| 札达县| 宁晋县| 赣州市| 油尖旺区| 肇州县| 电白县| 岫岩| 股票| 平遥县| 乌鲁木齐市| 花莲市| 赤峰市| 察隅县| 浠水县| 鹿泉市| 黄平县| 磐安县| 双江| 都昌县| 苏尼特右旗| 循化| 永登县| 光山县| 丰原市|