简单的发布订阅模式的通用实现
//发布订阅功能
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];
}
}
}
- 生成多个发布订阅对象会有多余的浪费。
- 考虑到有些异步请求,有可能发布了消息,而对该消息订阅的代码还没加载出,所以应该允许先订阅再发布。(设立一个存放离线事件的堆栈)
- 对于还没有被订阅的事件,在堆栈中保存发布的动作。等有对象来订阅事件时重新遍历发布堆栈里的事件。
- 对于离线事件,生命周期只有一次。
- 当订阅的事件多了也能产生命名冲突,应该允许创建命名空间。
//先发布后订阅
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]) {
}
}
}
})()