持续创作,加速成长!这是我参与「掘金日新计划 · 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类。
最后,放上自己比较喜欢的一句诗句:
千淘万漉虽辛苦,吹尽狂沙始到金 - 唐 刘禹锡《浪淘沙》