一道面试题
最近在看《JavaScript设计模式与开发实践》中的【发布订阅模式和观察者模式】。我不禁想起了上半年面试的时候一个面试官问我的问题:“你在实际项目中是如何处理非父子组件通信的?”
我回答的是:“大型项目的话一般都会用vuex,在一些小场景里会用EventEmitter”。
没想到面试官接着来了一句:“那你能手写代码实现一个简单的EventEmitter吗?”
手写WventEmitter
我想了一下,这主要是使用了emit发事件,用on去监听,还有off销毁事件监听,once实现单次事件处理…等等。考虑到时间紧张,我就只实现了收、发事件,移除监听的功能,有惊无险。。。
其实细想,这个和vue中内置实现的
$emit
、$on
是差不多的
且看下面代码:
class EventEmitter{
constructor() {
// 维护事件及监听者
this.listenners={}
}
/**
* 注册事件监听者
* @param {Object} type 事件类型
* @param {Object} cb 回调函数
*/
on(type,cb){
if(!this.listenners[type]){
this.listenners[type]=[]
}
this.listenners[type].push(cb)
}
/**
* 发布事件
* @param {String} type 事件类型
* @param {Function} cb 回调函数
*/
emit(type,...args){
if(this.listenners[type]){
this.listenners[type].forEach(cb=>{
cb(...args)
})
}
}
/**
* 移除某个事件的一个监听者
* @param {Object} type 事件类型
* @param {Object} cb 回调函数
*/
off(type,cb){
if(this.listenners[type]){
const targetIndex=this.listenners[type].findIndex(item=>item===cb)
if(targetIndex!==-1){
this.listenners[type].splice(targetIndex,1)
}
if(this.listenners[type].length===0){
delete this.listenners[type]
}
}
}
/**
* 移除某个事件的所有监听者
* @param {Object} type 事件类型
*/
offAll(type){
if(this.listenners[type]){
delete this.listenners[type]
}
}
}
有了这个自己实现的简单版本的EventEmitter
,我们就不用依赖第三方库了!
const mxc=new EventEmitter()
mxc.on('mxc',function(address,food){console.log('我饿了,我们取${address}吃${food}!')})
m服务器托管网xc.emit('mxc','南门','小火锅')
对了,这和Vue的:
const ee = new Vue();
ee.$on('chifan', function(address, food) { console.log(`吃饭了,我们去${address}吃${food}!`) })
ee.$emit('chifan', '三食堂', '铁板饭')
可是有异曲同工之妙!
再往下考虑就会发现,EventEmitter就是一个典型的发布订阅模式,实现了事件调度中心。
发布订阅模式中,包含发布者、事件调度中心、订阅者三个角色,我们刚刚实现的EventEmitter的一个实例:mxc,就是一个事件调度中心,发布者和订阅者之间是互不关心的,它们是松散耦合的。它们关注事件本身。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机服务器托管网房托管, http://www.fwqtg.net