面试官:说说Node中的EventEmitter?如何实现EventEmitter?转载本文请联系JS每日一问公众号.1.这是什么?我们了解到Node采用了事件驱动的机制,而EventEmitter是Node实现事件驱动的基础。在EventEmitter的基础上,Node的几乎所有模块都继承了这个类,这些模块都有自己的事件。绑定/触发监听器,实现异步操作。Node.js中很多对象都会分发事件,比如fs.readStream对象会在文件打开时触发事件。这些生成事件的对象是events.EventEmitter的实例。这些对象有一个eventEmitter.on()函数,用于将一个或多个函数绑定到命名事件。二、使用方法Node的事件模块只提供了一个EventEmitter类,它实现了Node的异步事件驱动架构。基本模式——观察者模式在该模式下,被观察者(主体)维护着一组由其他对象发送(注册)的观察者。如果一个新的对象对该主题感兴趣,就注册观察者,如果不感兴趣,就注册观察者。取消订阅,如果主题有更新,将依次通知观察者。基本代码如下:constEventEmitter=require('events')classMyEmitterextendsEventEmitter{}constmyEmitter=newMyEmitter()functioncallback(){console.log('event事件被触发了!')}myEmitter.on('event',callback)myEmitter.emit('事件')myEmitter.removeListener('事件',回调);通过实例对象的on方法注册一个名为event的事件,通过emit方法event触发事件,removeListener用于取消事件的监听。常用方法如下:emitter.addListener/on(eventName,listener):在事件数组末尾添加一个类型为eventName的监听事件emitter.prependListener(eventName,listener):将类型为eventName的监听事件添加到事件数组的headeremitter.emit(eventName[,...args]):触发类型为eventName的监听事件Eventemitter.removeListener/off(eventName,listener):移除eventName类型的监听事件emitter.once(eventName,listener):添加一个eventName类型的监听事件,以后只能执行一次,删除emitter.removeAllListeners([eventName]):移除所有类型为eventName的监听事件3.实现过程通过上面的方法,EventEmitter是一个构造函数,有一个对象classEventEmitter{constructor(){this.events={};}包含所有eventsinside}events中存放的事件监听函数结构如下:{"event1":[f1,f2,f3],"event2":[f4,f5],...}然后开始实现实例方法一步步来,首先emit,第一个参数是事件的类型,第二个参数是触发事件的函数的参数。实现如下:emit(type,...args){this.events[type].forEach((item)=>{Reflect.apply(item,this,args);});}实现emit后方法,然后实现on、addListener、prependListener三个实例方法,都添加了事件监听触发函数,实现类似on(type,handler){if(!this.events[type]){this.events[type]=[];}this.events[type].push(handler);}addListener(type,handler){this.on(type,handler)}prependListener(type,handler){if(!this.events[type]){this.events[type]=[];}this.events[type].unshift(handler);}后面是实现事件监听的方法removeListener/onremoveListener(type,handler){if(!this.events[类型]){return;}this.events[type]=this.events[type].filter(item=>item!==handler);}off(type,handler){this.removeListener(type,handler)}最后执行一次方法,在传入事件监听处理函数时进行封装,利用闭包的特性保持当前状态,通过fired属性值判断事件函数是否执行过once(type,handler){this.on(type,this._onceWrap(type,handler,this));}_onceWrap(type,handler,target){conststate={fired:false,handler,type,target};constwrapFn=this._onceWrapper.bind(state);状态.wrapFn=wrapFn;returnwrapFn;}_onceWrapper(...args){if(!this.fired){this.fired=true;Reflect.apply(this.handler,this.target,args);this.target.off(this.type,this.wrapFn);}}完整代码如下:classEventEmitter{constructor(){this.events={};}on(type,handler){if(!this.events[type]){this.events[type]=[];}this.events[type].push(handler);}addListener(type,handler){this.on(type,handler)}prependListener(type,handler){if(!this.events[type]){this.events[type]=[];}this.events[type].unshift(handler);}removeListener(type,handler){if(!this.events[type]){return;}this.events[type]=this.events[type].filter(item=>item!==handler);}off(type,handler){this.removeListener(type,handler)}emit(type,...args){this.events[type].forEach((item)=>{Reflect.apply(item,this,args);});}once(type,handler){this.on(type,this._onceWrap(type,handler,this));}_onceWrap(type,handler,target){conststate={fired:false,handler,type,target};constwrapFn=this._onceWrapper.bind(state);state.wrapFn=wrapFn;returnwrapFn;}_onceWrapper(...args){if(!this.fired){this.fired=true;Reflect.apply(this.handler,this.target,args);this.target.off(this.type,this.wrapFn);}}}测试代码如下:constee=newEventEmitter();//注册所有事件ee.once('wakeUp',(name)=>{console.log(`${name}1`);});ee.on('eat',(name)=>{console.log(`${name}2`)});ee.on('eat',(name)=>{console.log(`${name}3`)});constmeetingFn=(name)=>{console.log(`${name}4`)};ee.on('work',meetingFn);ee.on('work',(name)=>{console.log(`${name}5`)});ee.emit('wakeUp','xx');ee.emit('wakeUp','xx');//第二次没有触发ee.emit('eat','xx');ee.emit('工作','xx');ee.off('work',meetingFn);//移除事件ee.emit('work','xx');//再次工作参考http://nodejs.cn/api/events.html#events_class_eventemitterhttps://segmentfault.com/a/1190000015762318https://juejin.cn/post/6844903781230968845https://vue3js.cn/interview
