如何用騰訊云做網(wǎng)站seo百度關(guān)鍵詞優(yōu)化
文章目錄
- 1、現(xiàn)實(shí)中的發(fā)布-訂閱模式
- 2、DOM 事件
- 3、簡(jiǎn)單的發(fā)布-訂閱模式
- 4、通用的發(fā)布-訂閱模式
- 5、先發(fā)布再訂閱
- 6、小結(jié)
發(fā)布—訂閱模式又叫觀察者模式,它定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都將得到通知。在 JavaScript 開(kāi)發(fā)中,我們一般用事件模型來(lái)替代傳統(tǒng)的發(fā)布—訂閱模式
1、現(xiàn)實(shí)中的發(fā)布-訂閱模式
小明最近看上了一套房子,到了售樓處之后才被告知,該樓盤(pán)的房子早已售罄。好在售樓
處告訴小明,不久后還有一些尾盤(pán)推出。于是小明離開(kāi)之前,把電話號(hào)碼留在了售樓處,相同的還有小紅,小強(qiáng)。于是新樓盤(pán)推出的時(shí)候,售樓處會(huì)翻開(kāi)花名冊(cè),遍歷上面的電話號(hào)碼,依次發(fā)送一條短信來(lái)通知他們
2、DOM 事件
只要我們?cè)?jīng)在 DOM 節(jié)點(diǎn)上面綁定過(guò)事件函數(shù),那我們就曾經(jīng)使用過(guò)發(fā)布—訂閱模式
document.body.addEventListener( 'click', function(){ alert(2);
}, false ); document.body.click(); // 模擬用戶點(diǎn)擊
3、簡(jiǎn)單的發(fā)布-訂閱模式
發(fā)布-訂閱模式的實(shí)現(xiàn)步驟
1、定義發(fā)布者
2、給發(fā)布者添加一個(gè)緩存列表,用于存放回調(diào)函數(shù)以便通知訂閱者
3、最后發(fā)布消息的時(shí)候,發(fā)布者會(huì)遍歷這個(gè)緩存列表,依次觸發(fā)里面存放的訂閱者回調(diào)函數(shù)
代碼示例:
var salesOffices = {}; // 定義發(fā)布者
salesOffices.clientList = []; // 緩存列表,存放訂閱者的回調(diào)函數(shù)
salesOffices.listen = function (fn) {// 增加訂閱者this.clientList.push(fn); // 訂閱的消息添加進(jìn)緩存列表
};
salesOffices.trigger = function () {// 發(fā)布消息for (var i = 0; i < this.clientList.length; i++) {var fn = this.clientList.length;fn.apply(this, arguments); // arguments 是發(fā)布消息時(shí)帶上的參數(shù)}
};
測(cè)試:
salesOffices.listen(function (price, squareMeter) {// 小明訂閱消息console.log('小明價(jià)格= ' + price);console.log('小明squareMeter= ' + squareMeter);
});
salesOffices.listen(function (price, squareMeter) {// 小紅訂閱消息console.log('小紅價(jià)格= ' + price);console.log('小紅squareMeter= ' + squareMeter);
});salesOffices.trigger(2000, 300);
salesOffices.trigger(2000, 700);
問(wèn)題:
訂閱者接收到了發(fā)布者發(fā)布的每個(gè)消息,有些并不是訂閱者需要的
解決:
要增加一個(gè)標(biāo)示 key,讓訂閱者只訂閱自己感興趣的消息:
改寫(xiě)代碼:
var salesOffices = {}; // 定義發(fā)布者
salesOffices.clientList = {}; // 緩存對(duì)象,存放訂閱者的回調(diào)函數(shù)
salesOffices.listen = function (key, fn) {if (!this.clientList[key]) {// 如果還沒(méi)有訂閱過(guò)此類消息,給該類消息創(chuàng)建一個(gè)緩存列表this.clientList[key] = [];}this.clientList[key].push(fn); // 訂閱的消息添加進(jìn)消息緩存列表
};
salesOffices.trigger = function () {// 發(fā)布消息var key = Array.prototype.shift.call(arguments); // 取出消息類型var fns = this.clientList[key]; // 取出該消息對(duì)應(yīng)的回調(diào)函數(shù)集合if (!fns || fns.length === 0) {// 如果沒(méi)有訂閱該消息,則返回return false;}for (var i = 0; i < fns.length; i++) {var fn = fns[i];fn.apply(this, arguments); // (2) // arguments 是發(fā)布消息時(shí)附送的參數(shù)}
};
測(cè)試:
salesOffices.listen('squareMeter88', function (price) {// 小明訂閱 88 平方米房子的消息console.log('價(jià)格= ' + price); // 輸出: 2000000
});
salesOffices.listen('squareMeter110', function (price) {// 小紅訂閱 110 平方米房子的消息console.log('價(jià)格= ' + price); // 輸出: 3000000
});salesOffices.trigger('squareMeter88', 30000);
salesOffices.trigger('squareMeter110', 70000);
4、通用的發(fā)布-訂閱模式
包含:發(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);var fns = this.clientList[key];if (!fns || fns.length === 0) {return false;}for (var i = 0, fn; (fn = fns[i++]); ) {fn.apply(this, arguments);}},// 增加 remove 方法remove(key, fn) {var fns = this.clientList[key];if (!fns) {return false;}if (!fn) {fns && (fns.length = 0);} else {for (var i = fns.length - 1; i >= 0; i--) {var _fn = fns[i];if (fn === _fn) {fns.splice(i, 1);}}}},
};
測(cè)試:
var f1 = function (price) {console.log('價(jià)格= ' + price);
};
Event.listen('s88', f1);var f2 = function (price) {console.log('價(jià)格= ' + price);
};
Event.listen('s110', f2);Event.remove('s110', f2); // 刪除訂閱Event.trigger('s88', 30000);
Event.trigger('s110', 70000);
5、先發(fā)布再訂閱
應(yīng)用場(chǎng)景:發(fā)布者發(fā)布的內(nèi)容,不管訂閱者在發(fā)布之前訂閱,或者發(fā)布之后訂閱,都可觸發(fā)訂閱者訂閱的內(nèi)容
代碼:
var Event = (function () {var clientList = {};var offlineStack = {}; // 離線緩存參數(shù)var triggerStack = {}; // 已觸發(fā)trigger的參數(shù)緩存var listen;var trigger;var remove;listen = function (key, fn) {if (!clientList[key]) {clientList[key] = [];}clientList[key].push(fn);// 如果此時(shí)訂閱的事件,已經(jīng)發(fā)布了,則自定觸發(fā)一次訂閱內(nèi)容(fn)if (triggerStack[key]) {fn.apply(this, triggerStack[key]);} else if (offlineStack[key]) {// 如果是離線狀態(tài),則觸發(fā)事件fn.apply(this, offlineStack[key]);}};trigger = function () {var key = Array.prototype.shift.call(arguments);var fns = clientList[key];if (fns) {// 已經(jīng)有人訂閱此事件,將參數(shù)緩存//(假如有些訂閱者比較晚訂閱,且發(fā)布者已經(jīng)發(fā)布過(guò)了,那么這個(gè)訂閱者訂閱的時(shí)候,自動(dòng)觸發(fā)一次訂閱內(nèi)容)triggerStack[key] = [...arguments];for (var i = 0; i < fns.length; i++) {fns[i].apply(this, arguments);}} else {// 表示當(dāng)前還沒(méi)有人訂閱此事件,則先將參數(shù)緩存起來(lái)offlineStack[key] = [...arguments];}};// 取消訂閱remove = function (key, fn) {var fns = this.clientList[key];if (!fns) {return false;}if (!fn) {// 如果沒(méi)有傳入具體的回調(diào)函數(shù),表示需要取消 key 對(duì)應(yīng)消息的所有訂閱fns && (fns.length = 0);} else {for (var l = fns.length - 1; l >= 0; l--) {var _fn = fns[l];if (_fn === fn) {fns.splice(l, 1);}}}};return {listen: listen,trigger: trigger,remove: remove,};
})();
測(cè)試1:先訂閱,再發(fā)布
// 先訂閱
Event.listen('test1', function (a) {console.log('我是發(fā)布之前的訂閱者1:', a);
});
Event.listen('test1', function (a) {console.log('我是發(fā)布之前的訂閱者2:', a);
});
// 再發(fā)布
Event.trigger('test1', 12);// 我是發(fā)布之前的訂閱者1: 12
// 我是發(fā)布之前的訂閱者2: 12
測(cè)試2:先發(fā)布,再訂閱
// 先發(fā)布
Event.trigger('test1', 12);// 再訂閱
Event.listen('test1', function (a) {console.log('我是發(fā)布之后的訂閱者1:', a);
});
Event.listen('test1', function (a) {console.log('我是發(fā)布之后的訂閱者2:', a);
});// 我是發(fā)布之后的訂閱者1: 12
// 我是發(fā)布之后的訂閱者2: 12
測(cè)試3:先訂閱,再發(fā)布,再訂閱
// 先訂閱
Event.listen('lis1', function (a) {console.log('我是發(fā)布之前的訂閱者1:', a);
})
Event.listen('lis1', function (a) {console.log('我是發(fā)布之前的訂閱者2:', a);
})// 再發(fā)布
console.log('---第1次發(fā)布');
Event.trigger('lis1', 123);
console.log('---第1次發(fā)布完成');// 再訂閱
Event.listen('lis1', function (b) {console.log('我是發(fā)布之后的訂閱者~:', b);
})// ---第1次發(fā)布
// 我是發(fā)布之前的訂閱者1: 123
// 我是發(fā)布之前的訂閱者2: 123
// ---第1次發(fā)布完成
// 我是發(fā)布之后的訂閱者~: 123
測(cè)試4:先發(fā)布,再訂閱,再發(fā)布,再訂閱
// 先發(fā)布
console.log('------第1次發(fā)布-------');
Event.trigger('lis1', 123);// 再訂閱
Event.listen('lis1', function (a) {console.log('我是發(fā)布之后的訂閱者1:', a);
})
Event.listen('lis1', function (a) {console.log('我是發(fā)布之后的訂閱者2:', a);
})// 再發(fā)布
console.log('------第2次發(fā)布-------');
Event.trigger('lis1', 456);// 再訂閱
Event.listen('lis1', function (a) {console.log('我是發(fā)布之后的再次訂閱者1:', a);
})
Event.listen('lis1', function (a) {console.log('我是發(fā)布之后的再次訂閱者2:', a);
})// ------第1次發(fā)布-------
// 我是發(fā)布之后的訂閱者1: 123
// 我是發(fā)布之后的訂閱者2: 123// ------第2次發(fā)布-------
// 我是發(fā)布之后的訂閱者1: 456
// 我是發(fā)布之后的訂閱者2: 456
// 我是發(fā)布z之后的再次訂閱者1: 456
// 我是發(fā)布z之后的再次訂閱者2: 456
測(cè)試5:先訂閱,再發(fā)布,再訂閱,再發(fā)布
Event.listen('lis1', function (a) {console.log('我是發(fā)布之前的訂閱者1:', a);
})
Event.listen('lis1', function (a) {console.log('我是發(fā)布之前的訂閱者2:', a);
})console.log('---第1次發(fā)布');
Event.trigger('lis1', 123);
console.log('---第1次發(fā)布完成');Event.listen('lis1', function (b) {console.log('我是發(fā)布之后的訂閱者~:', b);
})console.log('---第2次發(fā)布');
Event.trigger('lis1', 456);
console.log('---第2次發(fā)布完成');// ---第1次發(fā)布
// 我是發(fā)布之前的訂閱者1: 123
// 我是發(fā)布之前的訂閱者2: 123
// ---第1次發(fā)布完成
// 我是發(fā)布之后的訂閱者~: 123// ---第2次發(fā)布
// 我是發(fā)布之前的訂閱者1: 456
// 我是發(fā)布之前的訂閱者2: 456
// 我是發(fā)布之后的訂閱者~: 456
// ---第2次發(fā)布完成
6、小結(jié)
優(yōu)點(diǎn):
一為時(shí)間上的解耦,二為對(duì)象之間的解耦
缺點(diǎn):
1、創(chuàng)建訂閱者本身要消耗一定的時(shí)間和內(nèi)存,而且當(dāng)你訂閱一個(gè)消息后,也許此消息最后都未發(fā)生,但這個(gè)訂閱者會(huì)始終存在于內(nèi)存中
2、如果過(guò)度使用的話,對(duì)象和對(duì)象之間的必要聯(lián)系也將被深埋在背后,會(huì)導(dǎo)致程序難以跟蹤維護(hù)和理解
應(yīng)用:
應(yīng)用非常廣泛,既可以用在異步編程中,也可以幫助我們完成更松耦合的代碼編寫(xiě)