發布—訂閱模式又叫觀察者模式,它定義對象間的一種一對多的依賴關系,當一個對象的狀 態發生改變時,所有依賴于它的對象都將得到通知。在 JavaScript 開發中,我們一般用事件模型 來替代傳統的發布—訂閱模式。
故事背景 小明最近看上了一套房子,到了售樓處之后才被告知,該樓盤的房子早已售罄。好在售樓 MM 告訴小明,不久后還有一些尾盤推出,開發商正在辦理相關手續,手續辦好后便可以購買。 但到底是什么時候,目前還沒有人能夠知道。 于是小明記下了售樓處的電話,以后每天都會打電話過去詢問是不是已經到了購買時間。除 了小明,還有小紅、小強、小龍也會每天向售樓處咨詢這個問題。一個星期過后,售樓 MM 決 定辭職,因為厭倦了每天回答 1000 個相同內容的電話。 當然現實中沒有這么笨的銷售公司,實際上故事是這樣的:小明離開之前,把電話號碼留在 了售樓處。售樓 MM 答應他,新樓盤一推出就馬上發信息通知小明。小紅、小強和小龍也是一 樣,他們的電話號碼都被記在售樓處的花名冊上,新樓盤推出的時候,售樓 MM 會翻開花名冊,遍歷上面的電話號碼,依次發送一條短信來通知他們。
發送短信通知就是一個典型的發布—訂閱模式,小明、小紅等購買者都是 訂閱者,他們訂閱了房子開售的消息。售樓處作為發布者,會在合適的時候遍歷花名冊上的電話號碼,依次給購房者發布消息。
代碼實現 var DEvent = (
function () {var clientList = {},listen,trigger,remove;listen =
function (key, fn) {
if (!clientList[key]) {clientList[key] = [];}clientList[key].push(fn);};trigger =
function () {var key = Array.prototype.shift.call(arguments),fns = clientList[key];
if (!fns || fns.length === 0) {
return false ;}
for (
let index = 0; index < fns.length; index++) {const fn = fns[index];fn.apply(this, arguments);}};remove =
function (key, fn) {var fns = clientList[key];
if (!fns) {
return false ;}
if (!fn) {fns && (fns.length = 0);}
else {
for (var l = fn.length - 1; l > 0 ; l--) {var _fn = fns[l];
if (_fn === fn) {fns.splice(l, 1);}}}};
return {listen,trigger,remove};
})();
Event.listen(
'squareMeter88' ,
function ( price ){ // 小紅訂閱消息console.log(
'價格= ' + price ); // 輸出:
'價格=2000000'
});
Event.trigger(
'squareMeter88' , 2000000 );// 售樓處發布消息
復制代碼 應用場景 網站登錄 假如我們正在開發一個商城網站,網站里有 header 頭部、nav 導航、消息列表、購物車等模塊。這幾個模塊的渲染有一個共同的前提條件,就是必須先用 ajax 異步請求獲取用戶的登錄信息。 這是很正常的,比如用戶的名字和頭像要顯示在 header 模塊里,而這兩個字段都來自用戶登錄后 返回的信息。 至于 ajax 請求什么時候能成功返回用戶信息,這點我們沒有辦法確定。現在的情節看起來像 極了售樓處的例子,小明不知道什么時候開發商的售樓手續能夠成功辦下來。 $.ajax(
'http:// xxx.com?login' ,
function (data){ // 登錄成功 login.trigger(
'loginSucc' , data); // 發布登錄成功的消息
});
var header = (
function (){ // header 模塊 login.listen(
'loginSucc' ,
function ( data){header.setAvatar( data.avatar );});
return {
set Avatar:
function ( data ){console.log(
'設置 header 模塊的頭像' );} }
})();var nav = (
function (){login.listen(
'loginSucc' ,
function ( data ){// nav 模塊 nav.setAvatar( data.avatar );});
return {
set Avatar:
function ( avatar ){ console.log(
'設置 nav 模塊的頭像' );} }
})();
復制代碼 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 ( ary, fn ){var ret;
for ( var i = 0, l = ary.length; i < l; i++ ){var n = ary[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[ key ] ){
if ( fn ){
for ( var i = cache[ key ].length; 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 ){var 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, 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;
})();
復制代碼 系列文章: 《JavaScript設計模式與開發實踐》最全知識點匯總大全
總結
以上是生活随笔 為你收集整理的《JavaScript设计模式与开发实践》模式篇(5)—— 观察者模式 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。