摘要:發(fā)布訂閱模式定義對(duì)象間的一種一對(duì)多的依賴關(guān)系當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí)所有依賴于它的對(duì)象都將得到通知簡(jiǎn)單實(shí)現(xiàn)定義發(fā)布者緩存列表存放訂閱者的回調(diào)函數(shù)定義訂閱者發(fā)布消息測(cè)試訂閱者價(jià)格訂閱者價(jià)格發(fā)布消息上面的實(shí)現(xiàn)方式導(dǎo)致了每個(gè)訂閱者都會(huì)收到發(fā)布
發(fā)布-訂閱模式
定義對(duì)象間的一種 一對(duì)多 的依賴關(guān)系, 當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí), 所有依賴于它的對(duì)象都將得到通知
簡(jiǎn)單實(shí)現(xiàn)
// 定義發(fā)布者 var salesOffices = {}; // 緩存列表, 存放訂閱者的回調(diào)函數(shù) salesOffices.clientList = []; // 定義訂閱者 salesOffices.listen = function (fn) { this.clientList.push(fn); } // 發(fā)布消息 salesOffices.trigger = function () { for (var i = 0, fn; fn = this.clientList[i++];) { fn.apply(this, arguments) } } /*** 測(cè)試 ***/ // 訂閱者1 salesOffices.listen(function (price, squareMeter) { console.log("價(jià)格=" + price); console.log("squareMeter= " + squareMeter); }); // 訂閱者2 salesOffices.listen(function (price, squareMeter) { console.log("價(jià)格=" + price); console.log("squareMeter= " + squareMeter); }); // 發(fā)布消息 salesOffices.trigger(2000, 80); salesOffices.trigger(3000, 100);
上面的實(shí)現(xiàn)方式, 導(dǎo)致了, 每個(gè)訂閱者都會(huì)收到發(fā)布者發(fā)布的消息
// 定義發(fā)布者 var salesOffices = {}; // 緩存列表, 存放訂閱者的回調(diào)函數(shù) salesOffices.clientList = {}; // 定義訂閱者 (增加緩存, 增加標(biāo)示 key ) salesOffices.listen = function (key, fn) { if (!this.clientList[key]) { this.clientList[key] = []; } this.clientList[key].push(fn); } // 發(fā)布消息 salesOffices.trigger = function () { var key = Array.prototype.shift.call(arguments), fns = this.clientList[key]; if (!fns || fns.length === 0) { return false; } for (var i = 0, fn; fn = fns[i++];) { fn.apply(this, arguments) } }提煉發(fā)布-訂閱模式
// 核心事件 var event = { clientList: {}, listen: function (key, fn) { if (!this.clientList[key]) { this.clientList[key] = []; } this.clientList[key].push(fn); }, trigger: function () { var key = Array.prototype.shift.call(arguments), fns = this.clientList[key]; if (!fns || fns.length === 0) { return false } for (var i = 0, fn; fn = fns[i++];) { fn.apply(this, arguments); } } } // 取消訂閱消息 event.remove = function (key, fn) { var fns = this.clientList[key]; if (!fns) { return false; } if (!fn) { // 沒有傳入fn 取消key對(duì)應(yīng)的所有的訂閱 fns && (fns.length = 0); } else { // 傳入fn 刪除對(duì)應(yīng)的fn for (var l = fns.length - 1; l >= 0; l--) { var _fn = fns[l]; if (_fn === fn) { fns.splice(l, 1) } } } } // 給任何對(duì)象動(dòng)態(tài)增加發(fā)布-訂閱功能 var installEvent = function (obj) { for (var key in event) { if (event.hasOwnProperty(key)) { obj[key] = event[key]; } } } /*** 測(cè)試 ***/ var salesOffices = {}; installEvent(salesOffices); // 訂閱者1 salesOffices.listen("squareMeter80", function (price) { console.log("價(jià)格=" + price); }); // 訂閱者2 salesOffices.listen("squareMeter100", function (price) { console.log("價(jià)格=" + price); }); // 發(fā)布消息 salesOffices.trigger("squareMeter80", 20000); salesOffices.trigger("squareMeter100", 30000);
登錄案例
// 登錄成功, 發(fā)布成功消息 $.ajax("http://xxx.com/login", function (data) { login.trigger("loginSuccess", data); }); // 這種寫法也很好 var header = (function () { // 訂閱消息 login.listen("loginSuccess", function (data) { header.setAvatar(data); }) return { setAvatar: function (data) { console.log("header 模塊拿到用戶信息") } } })();
以上寫法會(huì)有三個(gè)問題
需要一個(gè)類似"中介者"的角色, 把發(fā)布者和訂閱者聯(lián)系起來(通過 全局的 Event 來解決)
以上必須先訂閱, 才能發(fā)布
全局命名的沖突
實(shí)現(xiàn)類似 Event 的發(fā)布-訂閱模式優(yōu)點(diǎn)1: 時(shí)間上的解耦,
優(yōu)點(diǎn)2: 對(duì)象之間的解耦
缺點(diǎn)1: 創(chuàng)建訂閱者本生要消耗內(nèi)存和時(shí)間
缺點(diǎn)2: 弱化了對(duì)象之間的聯(lián)系之后, 對(duì)象之間的必要聯(lián)系也被埋沒
var Event = (function() { var global = this, Event, _default = "default"; Event = function () { var _listen, _trigger, _remove, _slice = Array.prototype.slice, _shift = Array.prototype.shift, _unshift = Array.prototype.unshift, namespaceCache = {}, _create, find, each = function(arr, fn) { var ret; for (var i = 0, l = arr.length; i < l; i++) { var n = arr[i]; ret = fn.call(n, i, n); } return ret; }; _listen = function (key, fn, cache) { if (!cache[key]) { cache[key] = []; } cache[key].push(fn); }; _remove = function (key, cache, fn) { if (cache[eky]) { if (fn) { for (var i = cache[key].length - 1; i >= 0; i--) { if (cache[key][i] === fn) { cache[key].splice(i, 1); } } } else { cache[key] = []; } } } _trigger = function () { var cache = _shift.call(arguments), key = _shift.call(arguments), args = arguments, _self = this, ret, stack = cache[key]; if (!stack || !stack.length) { return; } return each(stack, function () { return this.apply(_self, args); }) }; _create = function (namespace) { namespace = namespace || _default; var cache = {}, offlineStack = [], ret = { listen: function (key, fn, last) { _listen(key, fn, cache); if (offlineStack === null) { return; } if (last === "last") { offlineStack.length && offlineStack.pop()(); } else { each(offlineStack, function () { this(); }) } offlineStack = null; }, one: function (key, fn, last) { _remove(key, fn, cache); this.listen(key, fn, last); }, remove: function (key, fn) { _remove(key, cache, fn); }, trigger: function () { var fn, args, _self = this; _unshift.call(arguments, cache); args = arguments; fn = function () { return _trigger.apply(_self, args); } if (offlineStack) { return offlineStack.push(fn); } return fn(); } }; return namespace ? (namespaceCache[namespace] ? namespaceCache[namespace] : namespaceCache[namespace] = ret) : ret } return { create: _create, one: function (key, fn, last) { var event = this.create(); event.one(key, fn, last); }, remove: function (key, fn) { var event = this.create(); event.remove(key, fn); }, listen: function (key, fn, last) { var event = this.create(); event.listen(key, fn, last); }, trigger: function () { var event = this.create(); event.trigger.apply(this, arguments); } } }() return Event; })();觀察者(observer) 模式 和 發(fā)布/訂閱模式 之間的區(qū)別
本質(zhì)上的區(qū)別及時(shí)在調(diào)度的地方不同
分清楚誰是發(fā)布者, 誰是觀察者, 誰是訂閱者
觀察者模式
// subject(發(fā)布) 中的目標(biāo)發(fā)生變化. Observer(觀察) 能接受到變化 function ObserverList() { this.observerList = []; } ObserverList.prototype.add = function (obj) { return this.observerList.push(obj); } ObserverList.prototype.get = function (index) { if (index > -1 && this.observerList.length) { return this.observerList[index]; } } ObserverList.prototype.indexOf = function (obj, startIndex) { var i = startIndex; while(i < this.observerList.length) { if (this.observerList[i] === obj) { return i; } i++; } return -1; } ObserverList.prototype.removeAt = function (index) { this.observerList.splice(index, 1); } // 發(fā)布者 function Subject() { this.observers = new ObserverList(); } Subject.prototype.addObserver = function (observer) { this.observers.add(observer); } Subject.prototype.removeObserver = function (observer) { this.observers.removeAt(this.observers.indexOf(observer, 0)); } Subject.prototype.notify = function (context) { var observerCount = this.observers.count(); for (var i = 0; i < observerCount; i++) { this.observers.get(i).update(context); } } // 觀察者 function Observer() { this.update = function () { // ... } }
發(fā)布(Publish)/訂閱(Subscribe)模式
var pubsub = {}; (function (myObject) { var topics = {}; var subUid = -1; // 發(fā)布 myObject.publish = function (topic, args) { if (!topics[topic]) { return false; } var subscribers = topics[topic], len = subscribers ? subscribers.length : 0; while (len--) { subscribers[len].func(topic, args); } return this; } // 訂閱者 myObject.subscribe = function (topic, func) { if (!topics[topic]) { topics[topic] = []; } var token = (++subUid).toString(); topics[topic].push({ token: token, func: func }) } // 取消訂閱 myObject.unsubscribe = function (token) { for (var m in topics) { if (topics[m]) { for (var i = 0, j = topics[m].length; i < j; i++) { if (topics[m][i].token === token) { topics[m].splice(i, 1); return token; } } } } return this; } })(pubsub)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/104533.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ì)模式與開發(fā)實(shí)踐讀書筆記。發(fā)布訂閱模式又叫觀察者模式,它定義了對(duì)象之間的一種一對(duì)多的依賴關(guān)系。附設(shè)計(jì)模式之發(fā)布訂閱模式觀察者模式數(shù)據(jù)結(jié)構(gòu)和算法系列棧隊(duì)列優(yōu)先隊(duì)列循環(huán)隊(duì)列設(shè)計(jì)模式系列設(shè)計(jì)模式之策略模式 《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》讀書筆記。 發(fā)布-訂閱模式又叫觀察者模式,它定義了對(duì)象之間的一種一對(duì)多的依賴關(guān)系。當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴它的對(duì)象都將得到通知。 例...
摘要:雙向數(shù)據(jù)綁定簡(jiǎn)言之?dāng)?shù)據(jù)動(dòng)頁面動(dòng),頁面動(dòng),數(shù)據(jù)動(dòng)典型的應(yīng)用就是在做表單時(shí)候,輸入框的內(nèi)容改動(dòng)后,跟該輸入框的的值改動(dòng)。看官網(wǎng)上的這個(gè)的演示案例雙向數(shù)據(jù)綁定的好處要說出這個(gè)好處的時(shí)候,也只有在實(shí)際場(chǎng)景中才能對(duì)應(yīng)的顯示出來。 前言:本系列學(xué)習(xí)筆記從以下幾個(gè)點(diǎn)展開 什么是雙向數(shù)據(jù)綁定 雙向數(shù)據(jù)綁定的好處 怎么實(shí)現(xiàn)雙向數(shù)據(jù)綁定 實(shí)現(xiàn)雙向數(shù)據(jù)數(shù)據(jù)綁定需要哪些知識(shí)點(diǎn) 數(shù)據(jù)劫持 發(fā)布訂閱模式 ...
摘要:設(shè)計(jì)模式與開發(fā)實(shí)踐讀書筆記。看此文章前,建議先看設(shè)計(jì)模式之發(fā)布訂閱模式觀察者模式在中,已經(jīng)介紹了什么是發(fā)布訂閱模式,同時(shí),也實(shí)現(xiàn)了發(fā)布訂閱模式。 《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》讀書筆記。 看此文章前,建議先看JavaScript設(shè)計(jì)模式之發(fā)布-訂閱模式(觀察者模式)-Part1 在Part1中,已經(jīng)介紹了什么是發(fā)布-訂閱模式,同時(shí),也實(shí)現(xiàn)了發(fā)布-訂閱模式。但是,就Part1...
閱讀 2612·2021-11-16 11:40
閱讀 3409·2021-11-08 13:26
閱讀 870·2021-10-28 09:32
閱讀 3530·2021-09-13 10:26
閱讀 803·2019-08-30 15:55
閱讀 777·2019-08-30 15:44
閱讀 1908·2019-08-30 15:44
閱讀 1755·2019-08-30 13:48