今天在瀏覽JavaScript事件時,複習了下Dean Edward大神的addEvent。忽然以爲能夠基於他的思路實現一個結構更好的PubSub。app
思路也很簡單,就是要維護一個相似以下的一個倉庫結構:函數
/* { 'sayHello': { 0: fn0, 1: fn1, //... }, 'sayGoodBye': { 0: fnn, //... }, //... }*/
下面是個人實現代碼:測試
(function(exports) { var PubSub = exports.PubSub || {}; //在PubSub對象上增長靜態域PubSubCache,用於保存subscribe相關數據 /** * PubSub.PubSubCache倉庫結構 * { * 'sayHello': { * 0: fn0, * 1:fn1, * //..。 * }, * 'sayGoodBye': { * //... * } * } * */ PubSub.PubSubCache = PubSub.PubSubCache || {$uid: 0}; //PubSub有4個靜態方法:subscribe, subscribeOne, unsubscribe, publish //PubSub不會與DOM元素有關係。這樣publish也只能手動去觸發了 PubSub.subscribe = function(type, handler) { var cache = this.PubSubCache[type] || (this.PubSubCache[type] = {}); handler.$uid = handler.$uid || this.PubSubCache.$uid++; //把回調放入倉庫中 cache[handler.$uid] = handler; }; PubSub.unsubscribe = function(type, handler) { var counter = 0,$type, cache = this.PubSubCache[type]; if(arguments.length === 1) { //直接刪除此種類型的訂閱對象 if(!cache) return true; return !!this.PubSubCache[type] && (delete this.PubSubCache[type]); } else if(arguments.length === 2) { !!this.PubSubCache[type] && (delete this.PubSubCache[type][handler.$uid]); } //PubSubCahe倉庫中某類型訂閱爲空,則要刪除這個訂閱對象 for($type in cache) { counter++; } return !counter && (delete this.PubSubCache[type]); }; PubSub.publish = function(type) { var cache = this.PubSubCache[type], key, oneFlag, tmp, context, args = [].slice.call(arguments); if(!cache) return; if(args.length === 1) { context = exports; } else { context = args[1]; } //執行回調 for(key in cache) { tmp = cache[key]; //在發佈消息時能夠指定回調函數的上下文,同時還能夠傳入參數 cache[key].apply(context, args.slice(1)); tmp.one && this.unsubscribe(type, tmp); } }; PubSub.subscribeOne = function(type, handler) { this.subscribe(type, handler); //給函數加一個只執行一次的標誌 handler.one = true; }; exports.PubSub = PubSub; })(window);
下面是測試代碼:ui
var data = {name: 'haha', age:18}; var handler2 = function(data) { console.log('say.hello excuted! 2'); console.log(this.name); }; //訂閱say.hello消息 PubSub.subscribe('say.hello', function(data) { console.log('say.hello excuted! 1'); }); //第二次訂閱say.hello消息 PubSub.subscribe('say.hello', handler2); //第三次訂閱say.hello消息 PubSub.subscribe('say.hello', function(data) { console.log('say.hello excuted! 3'); console.log(this.age); }); //第四次增長一個只會執行一次的say.hello消息訂閱 PubSub.subscribeOne('say.hello', function(data) { console.log('say.hello excuted! one'); }); /** * 發佈say.hello消息類型 * 輸出: * say.hello excuted! 1 * say.hello excuted! 2 * haha * say.hello excuted! 3 * 18 * say.hello excuted! one */ PubSub.publish('say.hello', data); //取消第二次訂閱的say.hello類型 PubSub.unsubscribe('say.hello', handler2); /** * 發佈say.hello消息類型 * 輸出: * say.hello excuted! 1 * say.hello excuted! 3 * 18 */ console.log('--------------------------------') PubSub.publish('say.hello', data); /** * 再次發佈say.hello消息,不過此次除了傳入執行上下文外,還要傳入參數 * 輸出: * say.hello excuted! 1 * say.hello excuted! 3 * 18 * say.hello excuted! has deliverd args * args123 */ PubSub.subscribe('say.hello', function(data, args) { console.log('say.hello excuted! has deliverd args'); console.log(args); }); console.log('--------------------------------') PubSub.publish('say.hello', data, 'args123');
小結:this
全局的PubSub對象有四個方法:spa
1. subscribe(type, handler) :增長一種類型的消息訂閱。相似jQuery的bind();code
2. subscribeOne(type, handler):增長一種回調只會執行一次的消息訂閱,相似jQuery的one()對象
3. unsubscribe(type, [handler]): 若是隻傳type,會刪除全部的type消息訂閱;傳入了回調函數,則只刪除那一個回調。相似jQuery的unbind()blog
4. publish(type):執行訂閱。相似jQuery的trigger();事件
固然上面的功能Cowboy大神只用了只行代碼就實現了(基於jQuery):
(function($) { //獲得一個jQuery對象,以便使用其方法 var o = $({}); //爲jQuery對象增長靜態的方法 $.subscribe = function() { o.bind.apply(o, arguments); }; $.unsubscribe = function() { o.unbind.apply(o, arguments); }; $.publish = function() { o.trigger.apply(o, arguments); } })(jQuery);