设计模式--发布订阅模式 发表于 2017-03-13 简单的发布订阅模式的通用实现1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556//发布订阅功能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); } }, //取消订阅 remove:function(key,fn) { var fns=this.clientList[key]; if (!fns) { return false; } //没有传入回调,表示需要取消key对应的所有回调 if (!fn) { fns && (fns.length =0); } else { for (var i=0;i<fns.length;i++) { var _fn=fns[i]; if (fn===_fn) { fns.splice(i,1); } } } }} //为对象添加发布订阅var installEvent=function(obj) { for (var i in event) { if (event.hasOwnProperty(i)) { obj[i]=event[i]; } }} 生成多个发布订阅对象会有多余的浪费。 考虑到有些异步请求,有可能发布了消息,而对该消息订阅的代码还没加载出,所以应该允许先订阅再发布。(设立一个存放离线事件的堆栈) 对于还没有被订阅的事件,在堆栈中保存发布的动作。等有对象来订阅事件时重新遍历发布堆栈里的事件。 对于离线事件,生命周期只有一次。 当订阅的事件多了也能产生命名冲突,应该允许创建命名空间。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647//先发布后订阅Event.trigger('click',1);Event.listen('click',function(a) { console.log(a);})//使用命名空间Event.create('namespace1').listen('click',function(a) { console.log(a);})Event.create('namespace1').trigger('click',1);//代码实现var Event=(function() { var global=this, Event, _default='default'; Event=function() { var _listen, _trigger, _remove, _slice=[].slice, _shift=[].shift, _unshift=[],unshift, namespaceCache={}, _create, find, each=function(ary,fn) { for (var i=0;i<ary.length;i++) { var cur=ary[i]; fn.call(cur,cur,i); } }; _listen=function(key,fn,cache) { if (!cache[key]) { } } } })()