订阅-发布
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]);}}}}
