订阅-发布
event bus 既是 node 中各个模块的基石,又是前端组件通信的依赖手段之一,同时涉及了订阅-发布设计 模式,是非常重要的基础
// 将下列代码补充完整,可以使代码正确执行
class EventEmeitter {
constructor() {
this._events = this._events || new Map();
}
}
EventEmeitter.prototype.emit = function( type, ...args ) {
// your code
}
EventEmeitter.prototype.addListener = function(type, fn) {
// your code
}
EventEmeitter.prototype.removeListener = function(type, fn) {
// your code
}
const emitter = new EventEmeitter();
const say = name => {
console. log(`say: ${name}`);
}
emitter.addListener('say', say)
emitter.addListener('say', say);
emitter.emit('say', 'Hello World!');
emitter.removeListener('say', say);
emitter.emit('say', 'Hello World!');
// say: Hello World!
// say: Hello World!
实现
// 代码实现
class EventEmeitter {
constructor() {
// 选择了 Map 作为存储事件的结构,因为作为键值对的储存方式 Map 比一般对象更加适合,我们操作起来也更加简洁
this._events = this._events || new Map(); // 储存事件,回调键值对
this._maxListeners || 10; // 设置监听上限
}
}
// 触发名为 type 的事件
EventEmeitter.prototype.emit = function(type, ...args) {
let handler = this._events.get(type);
if (Array.isArray(handler)) {
// 如果是一个数组说明有多个监听者,需要依次触发;里面的函数
for (let i = 0; i < handler.length; i++) {
/* 触发监听函数我们可以用 apply 与 call 两种方法,在少数参数时 call 的性能更好,
* 多个参数时 apply 性能更好,当年 Node 的 Event 模块就在三个参数以下用 call 否则用 apply。
* Node 全面拥抱 ES6+ 之后,相应的 call/apply 操作用 Reflect 新关键字重写了
*/
if (args.length > 0) {
handler[i].apply(this, args);
} else {
handler[i].call(this);
}
}
} else if (handler && typeof handler === 'function') {
// 单个函数情况直接触发
if (args.length > 0) {
handler.apply(this, args);
} else {
handler.call(this);
}
}
// 返回自身,链式调用
return this;
}
// 监听名为 type 的事件
EventEmeitter.prototype.addListener = function(type, fn) {
// 获取对应事件的函数清单
const handler = this._events.get(type);
if (!handler) {
this._events.set(type, fn);
} else if (handler && typeof handler === 'function') {
// 如果 handler 是个函数,说明是一个监听者
this._events.set(type, [handler, fn]); // 多个监听者需要用数组存储
} else{
// 存在同类型监听者,直接往数组添加
handler.push(fn);
}
}
// 移除名为 type 的事件
EventEmeitter.prototype.removeListener = function(type, fn) {
// 获取对应事件的函数清单
const handler = this._events.get(type);
if (handler && typeof handler === 'function') {
// 如果 handler 是个函数,说明只被监听一次
this._events.delete(type, fn);
} else{
if (!Array.isArray(handler)) {
return this;
}
// 如果 handler 是数组,说明被监听多次,需要依次移除对应的函数
let position = handler.indexOf(fn);
// 如果找到匹配的函数,从数组清除
if (position !== -1) {
// 找到对应的位置,直接清除此回调
handler.splice(position, 1);
// 如果清除后只有一个函数,那么取消数组,以函数形式保存
if (handler.length === 1) {
this._events.set(type, handler[0]);
}
}
}
}