设计模式--发布订阅模式

简单的发布订阅模式的通用实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//发布订阅功能
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];
}
}
}
  • 生成多个发布订阅对象会有多余的浪费。
  • 考虑到有些异步请求,有可能发布了消息,而对该消息订阅的代码还没加载出,所以应该允许先订阅再发布。(设立一个存放离线事件的堆栈)
  • 对于还没有被订阅的事件,在堆栈中保存发布的动作。等有对象来订阅事件时重新遍历发布堆栈里的事件。
  • 对于离线事件,生命周期只有一次。
  • 当订阅的事件多了也能产生命名冲突,应该允许创建命名空间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//先发布后订阅
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]) {
}
}
}
})()