天天看點

你不知道的javascript設計模式(八)---- 釋出-訂閱模式前言正文小結

文章目錄

  • 前言
  • 正文
    • 釋出-訂閱模式的定義
    • 釋出-訂閱模式的實作
      • 售樓處的例子
      • 實作售樓處的例子
      • 取消訂閱的事件
    • javascript中實作釋出-訂閱模式的便利性
  • 小結

前言

        上一章我們介紹了一種簡單但是常見的設計模式,疊代器模式,也自己實作了一遍疊代器對象,這一章節要介紹的内容相信大家或多或少都耳濡目染過,那就是釋出-訂閱模式

正文

釋出-訂閱模式的定義

        釋出-訂閱模式又稱觀察者模式,它定義了對象間的一種一對多的依賴關系,當一個對象的狀态發生改變的時候,所有依賴它的對象都會得到通知。前幾章我們介紹的設計模式都分離了業務場景的特殊部分,那麼釋出-訂閱模式呢,沒錯,就如名字所示的那樣,釋出-訂閱模式分離了釋出和訂閱的功能,讓兩者間不再需要互相關注對方内部的邏輯

釋出-訂閱模式的實作

售樓處的例子

        在實作釋出-訂閱模式前,我們模拟一個場景,假設有一個售樓處,每天會有不同的顧客來訂閱樓盤的資訊,而售樓的小姐姐會在樓盤的價格資訊發生變化的時候,分别通知這些顧客,這就是一個明顯的釋出-訂閱模式的例子。可以發現,這個例子展現了釋出-訂閱模式的兩個明顯的優點:

  • 顧客不再需要主動一直向售樓小姐姐詢問樓盤的消息,也不需要關注什麼時候樓盤發生變化,售樓處會去監聽,顧客隻負責訂閱這份消息
  • 顧客和售樓處的耦合性降低,當有新的購房者出現的時候,隻需要把手機号留在售樓處,而售樓處也并不在意使用者的其他資訊

實作售樓處的例子

        在實作之前,我們先來縷縷這個場景釋出-訂閱實作的思路,無非就是下面三點:

  • 先建立釋出者對象(售樓處)
  • 給釋出者對象建立一個緩存數組,用來存放回調函數來通知不同的訂閱者(顧客)
  • 當需要釋出消息的時候,釋出者會周遊這個緩存數組,依次觸發裡面存放的訂閱者回調函數

順着上面的思路,我們不難封裝出下面的售樓處:

// 定義售樓處
const saleOffices = function() {
	this.clientList = []; 	// 緩存數組
	this.listen = function(fn) {		// 增加訂閱者
		this.clientList.push(fn);	// 訂閱的消息添加進緩存清單
	}
	this.trigger = function() { 	// 釋出消息
		for(let i = 0; i < this.clientList.length; i++) {
			let fn = this.clientList[i];
			fn.apply(this, arguments);
		}
	}
}
           

        這樣我們就已經實作好了一個簡單的釋出-訂閱模式,但是上面的實作還并不是完美的,還存在一些問題,并沒有對訂閱定制化,這樣會導緻可能你沒訂閱這個消息也會釋出消息,比如小陳隻想訂閱2号樓盤的價格變化,按上面的寫法不管是什麼樓盤都會去給小陳釋出通知消息,是以我們需要加一個标志的key,讓訂閱者隻收到他們感興趣的内容

// 定義售樓處
const saleOffices = function() {
	this.clientList = {};	// 緩存清單
	this.listen = function(key, fn) {		// 增加訂閱者
		if (!this.clientList[key]) {
			this.clientList[key] = [];
		}
		this.clientList[key].push(fn);	// 訂閱的消息添加進緩存清單
	}
	this.trigger = function() { 	// 釋出消息
		// 這裡使用arguments的原因是因為第一參數是key,後面可能還有一些釋出的參數需要一起給訂閱者
		let key = Array.prototype.shift.call(arguments), 
			fns = this.clientList[key], 
			len = fns.length;
		if (!fns || len === 0) {
			return false;
		}
		for(let i = 0; i < len; i++) {
			let fn = this.clientList[i];
			fn.apply(this, arguments);
		}
	}
}
           

        這樣就好多了,顧客終于可以訂閱自己感興趣的部分了,售樓處也會根據使用者的興趣去釋出對應的消息給顧客!

取消訂閱的事件

        假設有一天小陳又不感興趣這個樓盤了,或者已經買好了,但是售樓處還是照常給小陳發短信,小陳想取消掉之前他訂閱的事件應該怎麼辦呢,是以我們為售樓處額外再封裝一個remove方法,用于訂閱事件的取消

const saleOffices = function() {
	this.clientList = {};	
	this.listen = function(key, fn) {		
		if (!this.clientList[key]) {
			this.clientList[key] = [];
		}
		this.clientList[key].push(fn);	
	}
	this.trigger = function() { 	
		let key = Array.prototype.shift.call(arguments), 
			fns = this.clientList[key], 
			len = fns.length;
		if (!fns || len === 0) {
			return false;
		}
		for(let i = 0; i < len; i++) {
			let fn = this.clientList[i];
			fn.apply(this, arguments);
		}
	}
	// 取消訂閱的事件
	this.remove = function (key, fn) {
		let fns = this.clientList[key];
		if (!fns) {	// 如果key訂閱的消息沒有被人訂閱,則直接傳回
			 return false;
		}
		if (!fn) {	// 如果傳入的事件未定義,則認為是取消所有事件
			fns && (fns.length = 0);
		} else {
			// 注意要倒序周遊,正序周遊删除節點會導緻index錯亂,導緻後序的删除出錯
			for ( let i = fns.length - 1; i >= 0; i--) {
				let _fns = fns[i];
				if ( _fns = fns ) {
					fns.splice(i, 1); // 删除訂閱者的回調函數
				}
			}
		}
}
           

javascript中實作釋出-訂閱模式的便利性

        在java中實作釋出-訂閱模式,通常會把訂閱者本身當作引用傳入釋出者對象中,同時,訂閱者對象還需提供一個名為諸如update的方法,供釋出者調用。而在javascript中我們可以直接使用回調函數來代替傳統釋出-訂閱者模式,顯得更加優雅

        值得一提的是,vue中的雙向綁定源碼也是使用釋出-訂閱者模式去實作的,感興趣的同學可以到網上搜相關源碼去閱讀

小結

        這一章我們給大家介紹了釋出-訂閱模式,也稱觀察者模式,釋出-訂閱模式有着明顯的優勢:一、減少了程式時間上的耦合;二、減少了各子產品間的耦合;可以應用于異步等場景,可以幫助我們編寫更加松散耦合的代碼

        但是釋出-訂閱模式也有着它的缺陷,建立訂閱者會消耗一定的記憶體,并且當消息一直未釋出的時候,訂閱者也會一直存在,對記憶體會有一定的損耗,并且大量的訂閱者和釋出者交錯在一起,也會導緻bug的難以追尋蹤迹,難以判斷其源頭,是以也不能濫用釋出-訂閱模式

       小夥伴們今天的學習就到這裡了,如果覺得本文對你有幫助的話,歡迎轉發,評論,收藏,點贊!!!

       每天學習進步一點點,就是領先的開始。如果想繼續提高,歡迎關注我,或者關注公衆号”祯民講前端“。大量前端技術文章,面試資料,技巧等助你更進一步!

你不知道的javascript設計模式(八)---- 釋出-訂閱模式前言正文小結

繼續閱讀