折叠何宝莹

自定义事件机制和实现代码

最近看到一个题目挺有意思的。自定义事件,也就是说用 on 监听过后,使用 fire 可以触发事件。这个其实就是观察者模式,也叫做发布/订阅模型(pub/sub)

题目:

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

//评测题目: 实现自定义事件
// 编写一个简单的自定义事件处理器
// 1. 具备 on 方法绑定事件
// 2. 具备 off 方法解绑事件

function EventEmitter () {
// TODO

}

var emitter = EventEmitter();

emitter.on('foo', function(e){
console.log('listening foo event 1', e);
});

emitter.on('foo', function(e){
console.log('listening foo event 2', e);
});

emitter.on('bar', function(e){
console.log('listening bar event', e);
});

// 监听全部事件
emitter.on('*', function(e){
console.log('listening all events');
});

emitter.trigger('foo', {name : 'John'});
emitter.trigger('bar', {name : 'Sun'});
emitter.trigger('*', {name : 'Sun'});
emitter.off('foo’);

实现代码:

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
class EventEmitter {
constructor() {
// 使用 this.actions 作为一个容器, 存放相应的事件处理函数
this.actions = {}
}

// on 是绑定事件
on(type, action) {
// off 函数里是将 this.actions[type] 设置为 null
// 所以这里需要判断 this.actions[type] 是 undefined 或者 null 这两种情况
if (typeof this.actions[type] === 'undefined' || this.actions[type] === null) {
this.actions[type] = []
}
this.actions[type].push(action)
// return this 之后,就可以继续调用类的其他方法,这个就是所谓链式调用法
// $(element).hide().addClass('foo').siblings().removeClass('foo')
return this
}

trigger(...args) {
// 第一个 type 是 event type, 也就是触发的事件类型
// 剩下的所有参数都放在 rest 中
const [type, ...rest] = args
const actions = this.actions[type]
// 如果 actions 是数组, 就调用
if (Array.isArray(actions)) {
actions.forEach((f) => {
f.apply(this, rest)
})
}
var commonType = '*'
if (typeof this.actions[commonType] !== 'undefined' || this.actions[commonType] !== null) {
const commonActions = this.actions[commonType]
if (Array.isArray(commonActions)) {
commonActions.forEach((m) => {
m.apply(this, rest)
})
}
}
return this
}

// off 是解绑事件
off(type) {
// 如果传入了 type, 就移除 type 对应的 actions
// 否则移除所有的 actions
if (type !== undefined) {
this.actions[type] = null
} else {
this.actions = null
}
return this
}
}

还有一个问题我还没解决
就是监听全部事件的功能
// 监听全部事件

1
2
3
emitter.on('*', function(e){
console.log('listening all events');
});

暂时还不会写。但是思路可以记录一下,意思是监听了 * 以后,不管 trigger 什么事件都会触发这个回调。
试着添加这个功能啊。已经在代码上添加了。

题外话

之前看高程22章讲过这个内容,但是代码很糟糕,我贴一点出来就知道了。trigger的数据结构并没有定义过target和type,非常乱的代码。

1
2
3
4
5
6
7
8
9
10
11
function trigger(eventName) {
if (!eventName.target) {
eventName.target = this
}
if (this.handlers[eventName.type] instanceof Array) {
var handlers = this.handlers[eventName.type]
for (var i = 0, len=handlers.length; i < len; i++) {
handlers[i](eventName)
}
}
},