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