摘要:最常見的觀察者模式事件監(jiān)聽器這是最簡單最普通的一種觀察者模式,除此以外還有等。動畫在動畫中廣泛使用了觀察者模式,動畫的開始完成暫停等,都需要觀察者來確定物體的行為和狀態(tài)。參考資料設(shè)計模式發(fā)布訂閱模式
1. 最常見的觀察者模式Javascript活躍在事件驅(qū)動的環(huán)境中,比如鼠標(biāo)的響應(yīng)、事件的回調(diào)、網(wǎng)絡(luò)的請求等,觀察者模式又稱發(fā)布者-訂閱者(publisher-subscriber)模式,是處理對象及其行為和狀態(tài)之間的關(guān)系,管理人與任務(wù)之間的關(guān)系。
document.body.addEventListener("click", function () { console.log("you clicked me, poor guy!") });
這是最簡單最普通的一種觀察者模式,除此click 以外還有load、blur、drag、focus、mouseover、等。事件監(jiān)聽器(listener)有別于事件處理器(handler),在事件監(jiān)聽器中,一個事件可以關(guān)聯(lián)多個監(jiān)聽器,每個監(jiān)聽器獨立處理監(jiān)聽到的消息;事件處理器是執(zhí)行處理事件發(fā)生后的關(guān)聯(lián)函數(shù),一種事件是能有一個處理函數(shù):
var dom = $(".dom"); var listener1 = function(e){ //do one thing } var listener2 = function(e){ //do another thing } addEvent(dom,"click",listener1); addEvent(dom,"click",listener2);
在這個事件監(jiān)聽器的例子中,listener1和listener2 都是dom元素的監(jiān)聽器,當(dāng)dom被點擊時,都會執(zhí)行各自的函數(shù);
var dom = document.getElementById("dom"); var handler1 = function(e){ //do one thing } var handler2 = function(e){ //do another thing } dom.onclick = handler1; dom.onclick = handler2;
在這個事件處理器的例子中,handler1不會被執(zhí)行,只執(zhí)行handler2,是一次賦值的操作。
1.2 動畫在動畫中廣泛使用了觀察者模式,動畫的開始、完成、暫停等,都需要觀察者來確定物體的行為和狀態(tài)。
//定義動畫 var Animation = function(){ this.onStart = new Publisher; //關(guān)于Publisher的設(shè)計將在1.3節(jié)介紹 this.onComplete = new Publisher; this.onTween = new Publisher; } //定義一個原型方法 Animation.prototype.look = function(){ this.onStart.deliver("animation started!"); this.onTween.deliver("animation is going on!"); this.onComplete.deliver("animation completed!"); }; //實例一個box對象 var box = new Animation(); //定義三個函數(shù)作為subscribers var openBox = function(msg){ console.log(msg) } var checkBox = function(msg){ console.log(msg) } var closeBox = function(msg){ console.log(msg) } //訂閱事件 openBox.subscribe(box.onStart); checkBox.subscribe(box.onTween); closeBox.subscribe(box.onComplete); //調(diào)用方法 box.look() //animation started! //animation is going on! //animation completed!1.3 觀察者的構(gòu)建
首先,需要一個發(fā)布者。先定義一個構(gòu)造函數(shù),為其定義一個數(shù)組,用以保存訂閱者信息:
function Publisher(){ this.subscribes = []; }
發(fā)布者具有發(fā)布消息的功能,定義一個deliver的原型函數(shù):
Publisher.prototype.deliver = function(data){ this.subscribes.forEach(function(fn){ fn(data); }); return this; }
接下來構(gòu)造訂閱方法:
Function.prototype.subscribe = function(publisher){ var that = this; var alreadyExists = publisher.subscribes.some(function(el){ return el === that; }); if(!alreadyExists){ publisher.subscribes.push(this); } return this; }
直接在Function的prototype添加subscribe方法,這樣所有函數(shù)都可以調(diào)用該方法。這樣就構(gòu)建完畢了,使用方法參看1.2動畫的用例。
比較直觀的解釋(以onStart為例):當(dāng)box對象執(zhí)行look方法時,執(zhí)行onStart.deliver(),將onStart事件發(fā)布出去,廣播通知"animation started!",這個時候,一直在監(jiān)聽onStart的openBox監(jiān)聽到該事件發(fā)布的信息,打印出來。
這種方式模仿了nodejs的事件處理機制,代碼也比較簡潔:
var scope = (function() { //消息列表 var events = {}; return { //訂閱消息 on:function(name,hander){ var index = 0; //記錄消息時間的索引 if(events[name]){ //消息名已存在,將處理函數(shù)放到該消息的事件隊列中 index = events[name].push(hander) - 1; }else{ events[name] = [hander]; } //返回當(dāng)前消息處理事件的移除函數(shù) return function(){ events[name].splice(index,1); } }, //關(guān)閉消息 off:function(name){ if(!events[name]) return; //消息存在,刪除消息 delete events[name]; }, //消息發(fā)布 emit:function(name,msg){ //消息不存在,不處理 if(!events[name]) return; //消息存在,將該事件處理隊列中每一個函數(shù)都執(zhí)行一次 events[name].forEach(function(v,i){ v(msg); }); } } })(); var sayHello = scope.on("greeting",function(msg){ console.log("訂閱消息:" + msg); }); var greeting = function(msg){ console.log("發(fā)布消息:" + msg); scope.emit("greeting", msg); } greeting("hello Panfen!")1.5 nodejs中觀察者模式的實現(xiàn)方案
nodejs中有events模塊來實現(xiàn)觀察者模式,可參考Nodejs API-Events 談觀察者模式,大多數(shù)的模塊都集成了events模塊,所以可以直接使用emit發(fā)射事件和on監(jiān)聽事件,或者像下面這樣先定義一下;
var EventEmitter = require("events").EventEmitter; var life = new EventEmitter(); life.setMaxListeners(11); //設(shè)置最大監(jiān)聽數(shù),默認10 //發(fā)布和訂閱sendName life.on("sendName",function(name){ console.log("say hello to "+name); }); life.emit("sendName","jeff"); //發(fā)布和訂閱sendName2 function sayBeautiful(name){ console.log(name + " is beautiful"); } life.on("sendName2",sayBeautiful); life.emit("sendName2","jeff");
常用方法:
hasConfortListener :用于判斷發(fā)射的事件是否有監(jiān)聽器
removeListener :移除監(jiān)聽器
listenerCount :該事件所有監(jiān)聽器的總數(shù)
removeAllListeners :移除事件所有(或某個)的監(jiān)聽器
1.6 總結(jié)觀察者模式建立了推送和收聽的邏輯,適用于希望把人的行為和應(yīng)用程序的行為分開的場合。舉個例子來說:用戶點擊導(dǎo)航欄的一個tab時,會打開包含更多選項的子菜單,一般會選擇在知道哪個元素的情況下直接監(jiān)聽這個click事件,這樣做的弊端在于實現(xiàn)了與click事件直接綁在一起。更好的做法是:創(chuàng)建一個可觀察的onTabChange對象,關(guān)聯(lián)若干觀察者實現(xiàn)。
1.7 參考資料:《Javascript設(shè)計模式》
發(fā)布(Publish)/ 訂閱(Subscribe)模式
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/88182.html
摘要:下面為學(xué)習(xí)筆記,對觀察者模式做簡單實現(xiàn)。注冊的事件被觸發(fā)后需要執(zhí)行的動作注冊事件及對應(yīng)的執(zhí)行動作觸發(fā)事件對比執(zhí)行事件前后的事件列表內(nèi)容觀察者模式在解決類的耦合中的應(yīng)用小例子。 這篇筆記主要記錄學(xué)習(xí)思路及收獲,分享出來拋磚引玉,如有謬誤或優(yōu)化空間,歡迎交流。 要理解觀察者模式,可以類比vue中的EventBus,其實就是一個全局的觀察者對象($bus),上面有注冊事件($bus.on()...
摘要:總結(jié)一下從表面上看觀察者模式里,只有兩個角色觀察者被觀察者而發(fā)布訂閱模式,卻不僅僅只有發(fā)布者和訂閱者兩個角色,還有第三個角色經(jīng)紀(jì)人存在。參考鏈接觀察者模式發(fā)布訂閱模式 做了這么長時間的 菜鳥程序員 ,我好像還沒有寫過一篇關(guān)于設(shè)計模式的博客...咳咳...意外,純屬意外。所以,我決定,從這一刻起,我要把設(shè)計模式在從頭學(xué)習(xí)一遍,不然都對不起我這 菜鳥 的身份。那這次,就從觀察者模式開始好啦...
摘要:總結(jié)一下從表面上看觀察者模式里,只有兩個角色觀察者被觀察者而發(fā)布訂閱模式,卻不僅僅只有發(fā)布者和訂閱者兩個角色,還有第三個角色經(jīng)紀(jì)人存在。參考鏈接觀察者模式發(fā)布訂閱模式 做了這么長時間的 菜鳥程序員 ,我好像還沒有寫過一篇關(guān)于設(shè)計模式的博客...咳咳...意外,純屬意外。所以,我決定,從這一刻起,我要把設(shè)計模式在從頭學(xué)習(xí)一遍,不然都對不起我這 菜鳥 的身份。那這次,就從觀察者模式開始好啦...
摘要:設(shè)計模式是以面向?qū)ο缶幊虨榛A(chǔ)的,的面向?qū)ο缶幊毯蛡鹘y(tǒng)的的面向?qū)ο缶幊逃行┎顒e,這讓我一開始接觸的時候感到十分痛苦,但是這只能靠自己慢慢積累慢慢思考。想繼續(xù)了解設(shè)計模式必須要先搞懂面向?qū)ο缶幊蹋駝t只會讓你自己更痛苦。 JavaScript 中的構(gòu)造函數(shù) 學(xué)習(xí)總結(jié)。知識只有分享才有存在的意義。 是時候替換你的 for 循環(huán)大法了~ 《小分享》JavaScript中數(shù)組的那些迭代方法~ ...
摘要:寫代碼容易,寫出優(yōu)雅的代碼難,寫易于維護的容易擴展的結(jié)構(gòu)清晰的代碼應(yīng)該是每位開發(fā)者努力的目標(biāo),而學(xué)習(xí)設(shè)計模式,合理的的使用能讓我們離這個目標(biāo)更進一步。 寫代碼容易,寫出優(yōu)雅的代碼難,寫易于維護的、容易擴展的、結(jié)構(gòu)清晰的代碼應(yīng)該是每位開發(fā)者努力的目標(biāo),而學(xué)習(xí)設(shè)計模式,合理的的使用能讓我們離這個目標(biāo)更進一步。最近看了《Javascript設(shè)計模式與開發(fā)實踐》這本書,一言以蔽之,真不錯的一本...
閱讀 4167·2022-09-16 13:49
閱讀 1407·2021-11-22 15:12
閱讀 1529·2021-09-09 09:33
閱讀 1047·2019-08-30 13:15
閱讀 1732·2019-08-29 15:30
閱讀 665·2019-08-27 10:52
閱讀 2649·2019-08-26 17:41
閱讀 1904·2019-08-26 12:11