前言
几天前不是谈到了事件总线eventBus
吗, 然后对里面的监听器相关知识挺感兴趣。今天我用代码简单实现了下,大家可以看看是否有需要改进的地方。可以跳转到本文文末查看我的最终代码。
要求
实现一个监听器对象Listener类,需要具有四个方法:
on(event: String, fn: Function([data[, callback])])
对事件event
绑定一个监听器fn
once(event: String, fn: Function)
对事件event
绑定一个只执行一次的监听器fn
off(event: String [, fn: Function] )
- 只有第一个参数时,对事件
event
解绑所有监听器 - 有两个参数时, 对事件
event
解绑一个监听器fn
(这里的fn
需要与之前使用on
函数绑定的fn
指向同一个对象, 所以注意使用匿名函数做监听器函数的情况 参考eventBus
一节中提到的匿名函数问题)
- 只有第一个参数时,对事件
trigger(event: String, data: Object, callback: Function)
触发事件event
绑定的所有监听器,并传递参数data
及回调函数callback
(回调函数需要在on
绑定的函数fn
中自定义执行位置)
思路及代码
准备用一个Map
来维护事件名与监听器列表的对应关系。
注意在off
函数中有两种参数形式,以及在trigger
函数中注意移除一次性的监听器函数,代码如下:
// 判断是否为函数
function isFunction(fn) {
return typeof fn === 'function'
}
// 监听器类
function Listener() {
// 监听器Map, 维护事件名与监听器列表的对应关系
this.map = new Map();
/**
* 添加一个监听器
* (并没有去重,相同的监听器函数可多次添加)
* @param {string} name 事件名
* @param {function} fn 监听器函数
*/
this.on = function (name, fn) {
if (isFunction(fn)) {
const item = { once: false, fn };
this.map.set(name, this.map.has(name) ? this.map.get(name).push(item) : [item]);
} else {
throw new Error('请绑定函数')
}
}
/**
* 添加一个只执行一次的监听器
* @param {string} name 事件名
* @param {function} fn 监听器函数
*/
this.once = function (name, fn) {
if (isFunction(fn)) {
const item = { once: true, fn };
this.map.set(name, this.map.has(name) ? this.map.get(name).push(item) : [item]);
} else {
throw new Error('请绑定函数')
}
}
/**
* 移除监听器
* 若无第二个参数fn, 则移除所有监听器
* @param {string} name 事件名
* @param {function} fn 监听器函数
*/
this.off = function (name, fn) {
if (this.map.has(name)) {
if (!fn) {
this.map.delete(name);
} else if (isFunction(fn)) {
let items = this.map.get(name);
let index = items.findIndex(item => item.fn === fn);
if (index > -1) {
items.splice(index, 1);
}
} else {
throw new Error('请解绑函数')
}
}
}
/**
* 触发监听器
* @param {string} name 事件名
* @param {object} data 传递参数
* @param {function} callback 回调函数
*/
this.trigger = function (name, data, callback) {
if (this.map.has(name)) {
let items = this.map.get(name);
for (let len = items.length, i = len - 1; i >= 0; i--) {
let { once, fn } = items[i];
fn(data, callback);
if (once) {
items.splice(i, 1); // 移除只执行一次的监听器
}
}
}
}
}
// 测试
let l = new Listener();
l.on('event1', (data) => { // 每次使用trigger函数触发都会执行一次
console.log('event1 on', data);
})
// l.once('event1', (data) => { // 只执行一次,之后不再执行(已被移除)
// console.log('event1 once', data);
// })
l.trigger('event1', 1) // event1 1
l.trigger('event1', 2) // 不执行
l.trigger('event1', 3) // 不执行
let fn = (data, callback) => { // 定义监听器函数fn
console.log(data);
callback(data);
}
l.on('event2', fn)
console.log(l, l.map);
l.trigger('event2', 1, (res) => { // 传入数据 1, 并执行回调函数
console.log('callback1', res * 2); // callback1 2
})
l.off('event2', fn); // 移除事件‘event2’提仅有的监听器函数fn, 所以之后的trigger函数无法正确执行
l.trigger('event2', 2, (res) => { // 传入数据 2, 并执行回调函数
console.log('callback2', res * 3); // callback1 6
})
console.log(l, l.map);