如何自定义事件对象

之前因为开发中需要在自己的函数中增加一个事件处理机制。可惜在浏览器环境下只能自定义,如果在nodejs环境下可以继承自Event对象。

那么如何自己做一个Event类呢?

分析需求:

  • 绑定事件 : on
  • 触发事件 : fire
  • 移除事件 : remove

假设设计的api是这样的:

1
2
3
4
5
6
7
8
// 绑定事件
Event.on('some-event',handleFunc);

// 触发事件
Event.fire('some-event');

// 移除事件
Event.remove('some-event');

那么具体内部如何实现呢?

首先,我们需要一个东西保存这个事件名和事件名对应的函数。

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
function Event(){
this.eventList = {};
}

Event.prototype.on = function(eventName,fn){

// 不是string类型,就直接返回,不执行后面
if(typeof eventName != "string") return;

// 后面需要用到
var self = this;

// 如果有多个事件,分割出来
eventName = eventName.split(" ");

// 对每个事件进行遍历
eventName.forEach(function(val){
// 因为有可能对同一个事件绑定多个方法,那么就需要用一个数组来保存这个事件。
// 因此,第一次的时候这个保存这个事件的数组不存在,所以需要用下面的方式创建出来
self.eventList[val] = self.event[val] || [];

// 这里是当只有回调函数参数是函数时才会将其加入队列
if(typeof fn == "function"){
self.eventList[val].push(fn)
}
})
}

上面是绑定事件,其实按照思路写下来也是很简单的。下面是触发事件。

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
Event.prototype.fire = function(eventName){
if(typeof eventName != "string") return;

// 首先每个函数内部都可以取得一个`arguments`的类数组对象
// 该对象保存着所有传递进来的参数
// 也就是说,也可以通过arguments[0]来取得第一个传递进来的参数,以此类推。
// 那么往往有这样一个需求,在触发事件的时候,需要传递进来参数,但是通常不确实需要传递进来多少个参数,这时我们可以通过这个arguments来取得传递进来的参数。
// 但是有一个问题是,第一个参数是被触发的变量名,需要将第一个变量名剔除掉。

// 我们都知道数组对象有一个方法是`shift`,它会将数组的第一项去掉,并返回回来。
// 但是arguments只是类数组,并不是数组,需要将它转换为数组
// 最快的方式就是调用Array的slice方法,用call将slice方法的指针指向arguments,以此返回一个和arguments对象一样(参数一样)的数组
var args = Array.prototype.slice.call(arguments);
// 去掉第一个参数
args.shift();

// 取得事件数组
var eventList = this.eventList[eventName];

if(eventList instanceof Array){
eventList.forEach(function(val){
// 如果只有一个参数,确实可以直接调用的
// 但是因为参数是一个数组所以使用apply方法
val.apply(null,args)
})
}
}

到此,触发也就写好了,注意,这里我都是使用了forEach方法,这个方法在IE8以下不支持,需要改成for in方法。

其他的remove或者once等,也是在此基础上,比如remove就是找到之后删掉对应的方法,once是增加一个选项,在事件触发之后去掉该事件。