面试官:说说Node中的EventEmitter? 如何实现一个EventEmitter?

开发 前端
我们了解到,Node采用了事件驱动机制,而EventEmitter就是Node实现事件驱动的基础。

[[404630]]

本文转载自微信公众号「JS每日一题」,作者灰灰。转载本文请联系JS每日一题公众号。

一、是什么

我们了解到,Node采用了事件驱动机制,而EventEmitter就是Node实现事件驱动的基础

在EventEmitter的基础上,Node几乎所有的模块都继承了这个类,这些模块拥有了自己的事件,可以绑定/触发监听器,实现了异步操作

Node.js 里面的许多对象都会分发事件,比如 fs.readStream 对象会在文件被打开的时候触发一个事件

这些产生事件的对象都是 events.EventEmitter 的实例,这些对象有一个 eventEmitter.on() 函数,用于将一个或多个函数绑定到命名事件上

二、使用方法

Node的events模块只提供了一个EventEmitter类,这个类实现了Node异步事件驱动架构的基本模式——观察者模式

在这种模式中,被观察者(主体)维护着一组其他对象派来(注册)的观察者,有新的对象对主体感兴趣就注册观察者,不感兴趣就取消订阅,主体有更新的话就依次通知观察者们

基本代码如下所示:

const EventEmitter = require('events'
 
class MyEmitter extends EventEmitter {} 
const myEmitter = new MyEmitter() 
 
function callback() { 
    console.log('触发了event事件!'

myEmitter.on('event', callback) 
myEmitter.emit('event'
myEmitter.removeListener('event', callback); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

通过实例对象的on方法注册一个名为event的事件,通过emit方法触发该事件,而removeListener用于取消事件的监听

关于其常见的方法如下:

  • emitter.addListener/on(eventName, listener) :添加类型为 eventName 的监听事件到事件数组尾部
  • emitter.prependListener(eventName, listener):添加类型为 eventName 的监听事件到事件数组头部
  • emitter.emit(eventName[, ...args]):触发类型为 eventName 的监听事件
  • emitter.removeListener/off(eventName, listener):移除类型为 eventName 的监听事件
  • emitter.once(eventName, listener):添加类型为 eventName 的监听事件,以后只能执行一次并删除
  • emitter.removeAllListeners([eventName]):移除全部类型为 eventName 的监听事件

三、实现过程

通过上面的方法了解,EventEmitter是一个构造函数,内部存在一个包含所有事件的对象

class EventEmitter { 
    constructor() { 
        this.events = {}; 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

其中events存放的监听事件的函数的结构如下:


  "event1": [f1,f2,f3], 
  "event2": [f4,f5], 
  ... 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

然后开始一步步实现实例方法,首先是emit,第一个参数为事件的类型,第二个参数开始为触发事件函数的参数,实现如下:

emit(type, ...args) { 
    this.events[type].forEach((item) => { 
        Reflect.apply(item, this, args); 
    }); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

当实现了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); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

紧接着就是实现事件监听的方法removeListener/on

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) 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

最后再来实现once方法, 再传入事件监听处理函数的时候进行封装,利用闭包的特性维护当前状态,通过fired属性值判断事件函数是否执行过

once(type, handler) { 
    this.on(type, this._onceWrap(type, handler, this)); 
  } 
 
  _onceWrap(type, handler, target) { 
    const state = { fired: false, handler, type , target}; 
    const wrapFn = this._onceWrapper.bind(state); 
    state.wrapFn = wrapFn; 
    return wrapFn; 
  } 
 
  _onceWrapper(...args) { 
    if (!this.fired) { 
      this.fired = true
      Reflect.apply(this.handler, this.target, args); 
      this.target.off(this.type, this.wrapFn); 
    } 
 } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

完整代码如下:

class EventEmitter { 
    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) { 
        const state = { fired: false, handler, type , target}; 
        const wrapFn = this._onceWrapper.bind(state); 
        state.wrapFn = wrapFn; 
        return wrapFn; 
    } 
 
    _onceWrapper(...args) { 
        if (!this.fired) { 
            this.fired = true
            Reflect.apply(this.handler, this.target, args); 
            this.target.off(this.type, this.wrapFn); 
        } 
    } 

  • 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.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.

测试代码如下:

const ee = new EventEmitter(); 
 
// 注册所有事件 
ee.once('wakeUp', (name) => { console.log(`${name} 1`); }); 
ee.on('eat', (name) => { console.log(`${name} 2`) }); 
ee.on('eat', (name) => { console.log(`${name} 3`) }); 
const meetingFn = (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('work''xx'); 
ee.off('work', meetingFn);        // 移除事件 
ee.emit('work''xx');           // 再次工作 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

参考文献

 

  • http://nodejs.cn/api/events.html#events_class_eventemitter
  • https://segmentfault.com/a/1190000015762318
  • https://juejin.cn/post/6844903781230968845
  • https://vue3js.cn/interview

 

责任编辑:武晓燕 来源: JS每日一题
相关推荐

2024-03-28 10:37:44

IoC依赖注入依赖查找

2024-03-05 10:33:39

AOPSpring编程

2024-08-22 10:39:50

@Async注解代理

2021-05-20 08:34:03

CDN原理网络

2022-04-12 08:09:22

Nodejs前端面试题

2024-03-14 14:56:22

反射Java数据库连接

2024-07-31 08:28:37

DMAIOMMap

2024-12-06 07:00:00

2024-02-29 16:49:20

volatileJava并发编程

2024-03-22 06:56:24

零拷贝技术数据传输数据拷贝

2024-09-20 08:36:43

零拷贝数据传输DMA

2024-08-29 16:30:27

2024-08-12 17:36:54

2021-06-07 09:41:48

NodeBuffer 网络协议

2021-06-08 08:33:23

NodeStream数据

2021-06-10 07:51:07

Node.js循环机制

2021-06-03 08:14:01

NodeProcessJavaScript

2021-06-07 17:12:22

线程安全Atomic

2024-06-04 09:02:03

2025-02-28 00:00:00

点赞
收藏

51CTO技术栈公众号