- 如何理解:实现一个对象,是通信的中介,里面有三个方法:分别是on(监听)、off(关闭监听)、emit(触发)
- 例如:监听一个click事件,在三秒后触发某事件,代码雏形如下
const eventHub={
//这三个函数都没人关心其返回值,所以都return undefined
on:(name,fn)=>{return undefined},
emit:(name,data)=>{return undefined},
off:(name,fn)=>{return undefined}
}
eventHub.on('click',f1)
eventHub.off('click',f1)
setTimeout(()=>{
eventHub.emit('click','lanlan')
},3000)
- 实现on,即监听了某事件,就将该事件放入任务队列里(用哈希表map,因为任务队列里还有队列(如click队列、右键队列、左键队列等),即一个name对应一个数组,映射关系)
const eventHub={
map:{},
on:(name,fn)=>{
eventHub.map[name]=eventHub.map[name]||[] //先做判断,要入的这个队是否为空,若空则初始化
eventHub.map[name].push(fn) //入队
},
emit:(name,data)=>{return undefined},
off:(name,fn)=>{return undefined}
}
- 实现off,即取消监听某事件,就将该事件从任务队列里删除
const eventHub={
map:{},
on:(name,fn)=>{
eventHub.map[name]=eventHub.map[name]||[]
eventHub.map[name].push(fn)
},
emit:(name,data)=>{return undefined},
off:(name,fn)=>{
if(!eventHub.map[name]){return} //先做判断,若队列为空,直接return
const index=eventHub.map[name].indexOf(fn)
if(index<0){return} //若该事件不在队列里,直接return
eventHub.map[name].splice(index,1) //若在,删除即可
}
}
- off里的eventHub.map[name]可以用q代替(alias设计模式),但on里的不行,因为涉及到初始化改写
const eventHub={
map:{},
on:(name,fn)=>{
eventHub.map[name]=eventHub.map[name]||[]
eventHub.map[name].push(fn)
},
emit:(name,data)=>{return undefined},
off:(name,fn)=>{
const q=eventHub.map[name]
if(!q){return}
const index=q.indexOf(fn)
if(index<0){return}
q.splice(index,1)
}
}
- 实现emit,即调用队列里的函数
const eventHub={
map:{},
on:(name,fn)=>{
eventHub.map[name]=eventHub.map[name]||[]
eventHub.map[name].push(fn)
},
emit:(name,data)=>{
const q=eventHub.map[name] //map里可能是这样: click:[f1,f2],所以要遍历调用
if(!q){return} //也是要判断队列空不空,空的话直接return
q.map(f=>f.call(undefined,data)) //不空就直接遍历调用,forEach和map都是遍历,区别是map有返回值,forEach没有
return undefined
},
off:(name,fn)=>{
const q=eventHub.map[name]
if(!q){return}
const index=q.indexOf(fn)
if(index<0){return}
q.splice(index,1)
}
}
- 最终如下:
8. 还可以用class实现
class EventHub {
map = {}
on(name, fn) {
this.map[name] = this.map[name] || []
this.map[name].push(fn)
}
emit(name, data) {
const fnList = this.map[name] || []
fnList.forEach(fn => fn.call(undefined, data))
}
off(name, fn) {
const fnList = this.map[name] || []
const index = fnList.indexOf(fn)
if(index < 0) return
fnList.splice(index, 1)
}
}
// 使用
const e = new EventHub()
e.on('click', (name)=>{
console.log('hi '+ name)
})
e.on('click', (name)=>{
console.log('hello '+ name)
})
setTimeout(()=>{
e.emit('click', 'frank')
},3000)