摘要:姓名小強正式上班時間前端大大強訂閱了這個消息姓名大大強正式上班時間發(fā)布者發(fā)布消息前端小強姓名小強正式上班時間大大強姓名大大強正式上班時間通過添加了一個,我們實現(xiàn)了對職位的判斷。
觀察者模式,定義對象間的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都將得到通知。
事實上,只要你曾經(jīng)在DOM節(jié)點上綁定過事件函數(shù),那么你就曾經(jīng)使用過觀察者模式了!
document.body.addEventListener("click", function () { alert(2); });
但是這只是對觀察者模式最簡單的使用,在很多場景下我們經(jīng)常會實現(xiàn)一些自定義事件來滿足我們的需求。
舉個例子: 你去一家公司應(yīng)聘,談了一頓下來,hr跟你說:"好了,你回去等通知吧!"。 這個時候,1.你會問公司的電話,然后每天打過去問一遍結(jié)果 2.把自己的手機號留給hr,然后等他給你打電話 相信很多時候呢,大家都是選擇了后者。 萬一你每天給hr打電話弄煩他了,或許他本來打算招你的,現(xiàn)在也不再打算再鳥你啦! 那么這個時候,hr就相當(dāng)于一個發(fā)布者,而你就是一個訂閱者啦! 好吧,大部分叫你回去等消息的就等于沒救啦...... 我還遇到過一個如果你沒被錄取,就連通知都不通知你的公司!
那么一個簡單的觀察者模式應(yīng)該怎么實現(xiàn)呢?
要指定一個發(fā)布者;
給發(fā)布者添加一個緩存列表,用于存放回調(diào)函數(shù)以便通知訂閱者;(這家公司很多人來應(yīng)聘)
最后發(fā)布消息的時候,發(fā)布者會遍歷這個緩存列表,依次觸發(fā)里面存放的訂閱者回調(diào)函數(shù);(你up or 你over)
var event = {}; //發(fā)布者(hr) event.clietList = []; //發(fā)布者的緩存列表(應(yīng)聘者列表) event.listen = function(fn) { //增加訂閱者函數(shù) this.clietList.push(fn); }; event.trigger = function() { //發(fā)布消息函數(shù) for (var i = 0; i < this.clietList.length; i++) { var fn = this.clietList[i]; fn.apply(this, arguments); } }; event.listen(function(time) { //某人訂閱了這個消息 console.log("正式上班時間:" + time); }); event.trigger("2016/10",yes); //發(fā)布消息 //輸出 正式上班時間:2016/10
到這里,我們已經(jīng)實現(xiàn)了一個最簡單的觀察者模式了!
但是上面的函數(shù)其實存在一個問題,那就是發(fā)布者沒辦法選擇自己要發(fā)布的消息類型!
比如這家公司同時在招php,web前端,如果使用上面的函數(shù)就沒辦法區(qū)分職位了!只能一次性把全部訂閱者都發(fā)送一遍消息。
對上面的代碼進行改寫:
var event = {}; //發(fā)布者(hr) event.clietList = []; //發(fā)布者的緩存列表(應(yīng)聘者列表) event.listen = function(key, fn) { //增加訂閱者函數(shù) if (!this.clietList[key]) { this.clietList[key] = []; } this.clietList[key].push(fn); }; event.trigger = function() { //發(fā)布消息函數(shù) var key = Array.prototype.shift.call(arguments), fns = this.clietList[key]; for (var i = 0; i < fns.length; i++) { var fn = fns[i]; fn.apply(this, arguments); } }; event.listen("web前端", fn1 = function(time) { //小強訂閱了這個消息。 console.log("姓名:小強"); console.log("正式上班時間:" + time); }); event.listen("web前端", fn2 = function(time) { //大大強訂閱了這個消息 console.log("姓名:大大強"); console.log("正式上班時間:" + time); }); //發(fā)布者發(fā)布消息 event.trigger("web前端","小強", "2016/10"); //姓名:小強 正式上班時間:2016/10 event.trigger("php","大大強", "2016/15"); //姓名:大大強 正式上班時間:2016/15
通過添加了一個key,我們實現(xiàn)了對職位的判斷。
有了訂閱事件,我們怎么能少了取消訂閱事件呢?
event.remove = function(key, fn) { var fns = this.clietList[key]; if (!fns) { return false; } if (!fn) { //如果沒有傳入fn回調(diào)函數(shù),直接取消key對應(yīng)消息的所有訂閱 this.clietList[key] = []; } else { for (var i = 0; i < fns.length; i++) { //遍歷回調(diào)函數(shù)列表 var _fn = fns[i]; if (_fn === fn) { fns.splice(i, 1); //刪除訂閱者的回調(diào)函數(shù) } } } }; //這時候必須指定回調(diào)函數(shù),否則無法在remove函數(shù)中進行對比刪除。 event.listen("web前端", fn1 = function(time) { //小強訂閱了這個消息。 console.log("姓名:小強"); console.log("正式上班時間:" + time); }); event.listen("web前端", fn2 = function(time) { //大大強訂閱了這個消息 console.log("姓名:大大強"); console.log("正式上班時間:" + time); }); event.remove("web前端",fn1); //發(fā)布者發(fā)布消息 event.trigger("web前端","2016/10"); //輸出 姓名:大大強 正式上班時間:2016/10
對上面代碼進行改進,創(chuàng)建一個全局對象來實現(xiàn)觀察者模式,
使用閉包實現(xiàn)私有變量,僅暴露必須的API給使用者:
var event = (function() { var clietList = []; //發(fā)布者的緩存列表(應(yīng)聘者列表) var listen = function(key, fn) { //增加訂閱者函數(shù) if (!this.clietList[key]) { this.clietList[key] = []; } this.clietList[key].push(fn); }; var trigger = function() { //發(fā)布消息函數(shù) var key = Array.prototype.shift.call(arguments), fns = this.clietList[key]; for (var i = 0; i < fns.length; i++) { var fn = fns[i]; fn.apply(this, arguments); } }; var remove = function(key, fn) { var fns = this.clietList[key]; if (!fns) { return false; } if (!fn) { //如果沒有傳入fn回調(diào)函數(shù),直接取消key對應(yīng)消息的所有訂閱 this.clietList[key] = []; } else { for (var i = 0; i < fns.length; i++) { //遍歷回調(diào)函數(shù)列表 var _fn = fns[i]; if (_fn === fn) { fns.splice(i, 1); //刪除訂閱者的回調(diào)函數(shù) } } } }; return{ listen:listen, trigger:trigger, remove:remove } })();
觀察者模式進階:
使用命名空間防止事件名沖突
實現(xiàn)先發(fā)布后訂閱功能
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/91025.html
摘要:觀察者模式也稱發(fā)布訂閱模式它的作用就是當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都將得到通知,自動刷新對象狀態(tài)舉個生活比較常見常見的例子比如你去面試之后,面試官看你表現(xiàn)不錯,最后會跟你要聯(lián)系方式,以便之后可以聯(lián)系你。 觀察者模式也稱發(fā)布-訂閱模式,它的作用就是當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都將得到通知,自動刷新對象狀態(tài) 舉個生活比較常見常見的例子,比如你去面試之后,...
摘要:它通過數(shù)據(jù)模型進行鍵值綁定及事件處理,通過模型集合器提供一套豐富的用于枚舉功能,通過視圖來進行事件處理及與現(xiàn)有的通過接口進行交互。 本人兼職前端付費技術(shù)顧問,如需幫助請加本人微信hawx1993或QQ345823102,非誠勿擾 1.為初學(xué)前端而不知道怎么做項目的你指導(dǎo) 2.指導(dǎo)并扎實你的JavaScript基礎(chǔ) 3.幫你準(zhǔn)備面試并提供相關(guān)指導(dǎo)性意見 4.為你的前端之路提供極具建設(shè)性的...
摘要:寫在前面這一章的順序?qū)τ谖唇佑|過使用過的童鞋而言略抽象了,前邊幾章主要為了說明和之前的異步方式相比有什么優(yōu)勢和它能解決什么問題,后邊才詳解的設(shè)計和各種場景下如何使用。建議先了解和簡單使用過后再閱讀,效果更佳。 寫在前面:Promise這一章的順序?qū)τ谖唇佑|過使用過Promise的童鞋而言略抽象了,前邊幾章主要為了說明Promise和之前的異步方式相比有什么優(yōu)勢和它能解決什么問題,后邊才...
摘要:模型與視圖解耦,編寫單元測試更方便。切實的將模型綁定到了視圖,這一責(zé)任在中被控制器提前持有了。視圖和視圖模型使用數(shù)據(jù)綁定和事件進行通信。觸發(fā)器數(shù)據(jù)觸發(fā)器允許我們進一步在視圖狀態(tài)變化后改變我們的對象屬性。 MVC、MVP 和 MVVM 三個非常重要的架構(gòu)模式 MVC (Model(模型)-View(視圖)-Controller(控制器)) MVP (Model(模型)-View(視圖)...
閱讀 1664·2019-08-30 15:55
閱讀 985·2019-08-30 15:44
閱讀 878·2019-08-30 10:48
閱讀 2052·2019-08-29 13:42
閱讀 3193·2019-08-29 11:16
閱讀 1277·2019-08-29 11:09
閱讀 2063·2019-08-26 11:46
閱讀 624·2019-08-26 11:44