怎么实现系列:8.怎么实现一个eventEmitter-上篇

147 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情

系列介绍

经常会被面试官问到这个功能或者效果怎么实现,或者怎么实现xx功能/效果?

有些功能或者效果以前没有做过或者实现过,导致自己回答不出来,场面一度变得有些尴尬。

于是自己打算出一个怎么实现系列,来实现xx功能或者xx效果,以此来提升自己的实战效果和能力。

本系列是一个开放系列,可能自己能力有限,故我实现的效果或者代码不一定是最好的,欢迎大家参与交流讨论。

场景

面试官:有了解过eventEmitter吗?

我:有的,node里面的events模块就有一个eventEmitter类。eventEmitter类是node事件驱动系统的基础,可以用eventEmitter来监听或者触发事件,从而实现node的异步事件驱动架构。

面试官:你应该有使用过eventEmitter,那让你实现一个eventEmitter,你怎么实现?

(假装思考了一会)

我:这个,我虽然使用过eventEmitter,但是要说实现一个eventEmitter,倒是没有实现过。

面试官:...

查阅资料

eventEmitter,事件发布订阅系统。

我们可以利用eventEmitter来监听事件,当事件触发时,执行对应的回调函数。

我们也可以使用eventEmitter来触发事件。

eventEmitter既可以监听事件,也可以触发事件,是一种事件驱动机制。它的监听事件和触发事件,组成了一个事件发布订阅系统。

实现

先创建一个index.js文件,我们将在这个文件里,创建一个eventEmitter类。

定义类

先定义一个eventEmitter类

class EventEmitter {
}

这里,我们使用es6的类写法。当然,你也可以使用构造函数的形式来实现。

定义构造函数

constructor() {
     this.queue = {}
}

给实例对象添加一个queue属性,初始值为一个空对象。

queue该属性是整个eventEmitter的核心,eventEmitter监听事件或者触发事件,都需要用到这个queue对象。所以大家要理解掌握。

定义on方法

on方法,用来监听事件


on(event,cb) {
        this.queue[event] = this.queue[event] || []
        this.queue[event].push(cb)
}

先是往queue对象里取对应事件属性值,如果有值,就直接取值出来。如果没值,则赋值一个初始值--空数组。

然后往事件数组里,添加回调函数。

定义emit方法

emit方法,用来触发事件


emit(event,...args){
        if(this.queue[event]){
            this.queue[event].forEach(cb=>{
                cb(args)
            })
        }
}

queue对象里,取出对应事件数组。遍历事件数组,把里面的回调函数拿出来,一一执行。

定义removeListener方法

removeListener方法,用来移除事件监听


 removeListener(event,cb){
        if(this.queue[event]){
            let index = this.queue[event].findIndex(item=>{
                return item === cb
            })
            this.queue[event].splice(index,1)
        }
  }

先是从queue对象里取出对应事件数组,从事件数组里,找出对应回调函数的下标。

然后,通过下标,把回调函数从事件数组里移除。

定义removeAllListener方法

removeAllListener方法,移除所有事件监听。


removeAllListener(){
        this.queue = {}
}

这里,我是直接把queue赋值为一个空对象,即初始值。该方法比较简单。

当然,你也可以使用其它方法,保证它的事件数组为空或者为空数组。

定义once方法

once方法,让监听事件的回调函数只执行一次


once(event,cb){
        function callback(){
            cb.call(this)
            this.removeListener(event,callback)
        }
        this.queue[event] = this.queue[event] || []
        this.queue[event].push(callback.bind(this))
    }

once里面,再定义一个函数callback

callback函数里,先是把回调函数执行一次,然后就把callback从事件数组里移除。

从而实现监听事件的回调函数只执行一次。

完整代码

class EventEmitter {
    constructor() {
        this.queue = {}
    }
    on(event,cb) {
        this.queue[event] = this.queue[event] || []
        this.queue[event].push(cb)
    }
    emit(event,...args){
        if(this.queue[event]){
            this.queue[event].forEach(cb=>{
                cb(args)
            })
        }
    }
    removeListener(event,cb){
        if(this.queue[event]){
            let index = this.queue[event].findIndex(item=>{
                return item === cb
            })
            this.queue[event].splice(index,1)
        }
    }
    removeAllListener(){
        this.queue = {}
    }
    once(event,cb){
        function callback(){
            cb.call(this)
            this.removeListener(event,callback)
        }
        this.queue[event] = this.queue[event] || []
        this.queue[event].push(callback.bind(this))
    }
}

小结

在本篇文章中,我们主要实现了一个eventEmitter类。

该eventEmitter类,有如下方法

  • constructor
  • on
  • emit
  • removeListener
  • removeAllListener
  • once

我们主要先实现eventEmitter类,在下篇文章里,我们再看下怎么使用该eventEmitter类。

最后,放上自己比较喜欢的一句诗句:

千淘万漉虽辛苦,吹尽狂沙始到金 - 唐 刘禹锡《浪淘沙》