✊不积跬步,无以至千里;不积小流,无以成江海
思路
发布订阅本质是一种设计模式,是一个代码的模板。
具体实现思路:
- 首先定义一个变量eventHub,作为一个接口实现我们通讯的中介。
- 这个变量有三个固定的接口,on emit off
- 在函数外面通过方法来调用它们,比如:有些时候通过点击去召唤eventHub.on;某些时刻不需要这个函数让它关闭eventHub.off;在3s之后触发eventHub.emit函数等方式
- 关注一下三个变量的输入和输出。输入:事件名字(类型),以及它的函数/数据。输出:on因为没人关心他的返回值,所以return undefined;off和on非常类似,所以也是return undifined;emit好像也没人关心它的返回值,因此还是undefined
- 接下来看每个函数需要实现的功能
- on:如果监听了一个事件,就把它放到任务队列里面。因此要定义一个队列,但是由于这个队列的内容和结果是一一映射的,所以其实这个队列是一个哈希表,定义为map。之后实现on,拿到当前队列中的第name个数据并push到fn中即可。但是由于我们要实现防御式编程,即去判断会不会在哪里出错,那么我们需要在编程之前进行判断,如果这个队列为undefined,我们就要初始化这个队列。
- 由于on和off比较类似,因此再实现一下off。off不需要初始化(因为一定存在了才off),但需要判断是否为空,如果为空就直接返回。如果不为空,就用indexOf去找队列中是否含有含有fn这个函数。如果index小于0,则直接return;如果没有小于0,则利用splice方法,输入索引+参数,达到删掉哪里删掉几个的功能。
- 最后实现emit的功能。首先判断是不是为空,如果为空直接return。如果不为空就用map遍历一下,对队列中的每一个f函数进行调用,再传入data
- 因为on/off/emit的返回都是undefined,所以可以删掉所有的return undefined
实现
const eventHub = {
map: {},
on: (name, fn)=>{
//入队
//if (eventHub.map[name] === undefined){
// eventHub.map[name] = []}
// 将上面的内容转化为1行
eventHub.map[name] = eventHub.map[name] || []
eventHub.map[name].push(fn)
//return undefined
},
emit: (name, data)=>{
if (!eventHub.map[name]) {return}
eventHub.map[name].map(f => f(data))
//return undefined
},
off: (name, fn)=>{
if (!eventHub.map[name]) {return}
const index = eventHub.map[name].indexOf(fn)
if(index < 0){return}
eventHub.map[name].splice(index,1)
//return undefined
}
}
eventHub.on('click',f1)
eventHub.off('click',f1)
setTimeout(()=>{
eventHub.emit('click', f2)
}, 3000)
///
eventHub.on('click', console.log)
eventHub.on('click',console.error)
setTimeout(()=>{
eventHub.emit('click', 'hello')
}, 3000)
也可以先on再off一下