【第八章】 发布-订阅模式

优点:

  1. 时间上的解耦
  2. 对象之间的解耦

缺点: 消耗时间和内存

自定义事件

如何实现发布-订阅模式:

  1. 指定发布者
  2. 给发布者添加一个缓存列表,用于存放回调函数 以便通知 订阅者
  3. 发布消息的时候,发布者会遍历这个缓存列表,依次触发存放的订阅者回调函数
    (还可以在回调函数中填入一些参数,订阅者可以接收这些参数。eg:售楼处可以在给订阅者短信内加上房子的单价、面积等信息)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var salesOffices = {}   // 售楼处
salesOffices.clientList = [] // 缓存列表,存放订阅者的回调函数
salesOffices.listen = function(fn){ // 增加订阅者
this.clientList.push(fn)
}
salesOffices.trigger = function(){ // 发布消息
for(var i=0;i<this.clientList.length;i++){
var fn = this.clientList[i]
fn.apply(this,arguments)
}
}

salesOffices.listen(function(price,squareMeter){ // 小明订阅消息
console.log('m 价格= '+price)
console.log('m 平方= '+squareMeter)
})

salesOffices.listen(function(price,squareMeter){ // 小红订阅消息
console.log('h 价格= '+price)
console.log('h 平方= '+squareMeter)
})

salesOffices.trigger(20000,22) // m/h 价格= 20000 m/h 平方= 22

我们看到订阅者接收到了发布者发布的每个消息,虽然小明只想买 88 平方米的房子,但是发布者把 122 平方米的信息也推送给了小明,这对小明来说是不必要的困扰。所以我们有必要增加一个标示 key,让订阅者只订阅自己感兴趣的消息。改写后的代码如下:

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
var salesOffices = {}   // 售楼处
salesOffices.clientList = [] // 缓存列表,存放订阅者的回调函数
salesOffices.listen = function(key,fn){ // 增加订阅者
if(!this.clientList[key]){ // 如果没有订阅此类消息,给该类创建一个缓存列表
this.clientList[key] = []
}
this.clientList[key].push(fn)
}
salesOffices.trigger = function(){ // 发布消息
var key = Array.prototype.shift.call(arguments) // 取出消息类型 <=> arguments[0]
var fns = this.clientList[key] // 取出该消息对应的回调函数集合
for(var i=0;i<fns.length;i++){
var fn = fns[i]
fn.apply(this,arguments)
}
}

salesOffices.listen('squareMeter88',function(price){
console.log('m 价格= '+price)
})
salesOffices.listen('squareMeter88',function(price){
console.log('w 价格= '+price)
})
salesOffices.listen('squareMeter122',function(price){
console.log('h 价格= '+price)
})

salesOffices.trigger('squareMeter88',2200) // m/w 价格= 2200
salesOffices.trigger('squareMeter122',122000) // h 价格= 122000

发布-订阅模式的通用实现

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
// 把发布-订阅功能提取出来,放在一个单独的对象内
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)
var fns = this.clientList[key]
if(!fns || fns.length===0){
return false
}
for(var i=0;i<fns.length;i++){
var fn = fns[i]
fn.apply(this,arguments)
}
}
}
// installEvent 函数,给所有对象动态安装 发布-订阅功能:
// 浅拷贝 (我感觉 没什么用。。)
function installEvent(obj){
for(var i in event){
obj[i] = event[i]
}
}
var salesOffices = {}
installEvent(salesOffices)
salesOffices.listen('squareMeter88',function(price){
console.log('price= '+ price)
})
salesOffices.trigger('squareMeter88',122)

取消订阅事件

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
var event = {
...
remove: function(key,fn){
var fns = this.clientList[key]
if(!fns){
return false
}
if(!fn){ // 如果没有传入具体的回调函数,表示需要取消 key 对应消息的所有订阅
fns && (fns.length = 0)
}else{
for(var i=fns.length-1;i>=0;i--){ // 倒序删除
var _fn = fns[i]
if(_fn===fn){
fns.splice(i,1)
}
}
}
}
}
...
salesOffices.listen('squareMeter88',fn1 = function(price){
console.log('f1 price= '+ price)
})
salesOffices.listen('squareMeter88',fn2 = function(price){
console.log('f2 price= '+ price)
})
salesOffices.listen('squareMeter88',fn1 = function(price){
console.log('f3 price= '+ price)
})
salesOffices.remove('squareMeter88',fn1) // 删除的是最后一个叫f1的订阅者
salesOffices.trigger('squareMeter88',122) // f1/f2 price= 122

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2019-2023 John Doe
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信