面试官:手写一个Bus总线/EventBus(发布订阅模式的简单实现)

127 阅读1分钟


小二,上酒!

天涯的渡口, 一人陪马儿清瘦, 多么富有, 天下了心中。


发布订阅模式

我们在项目中使用的事件总线$Bus其实就是发布订阅模式,还有一个和它很像的观察者模式

我们在vue项目中可能这样使用Bus来兄弟组件通信

//Bus.js
import Vue from 'vue'
const Bus = new Vue();
export default Bus;
//组件1
import Bus from './Bus';
//注册事件  可以写在mounted生命周期中
Bus.$on('add',(val)=>{
    console.log(val);
    //逻辑
})
//组件2
import Bus from './Bus'
//触发事件  可以在methods的逻辑方法中
Bus.$emit('add',100)
//组件3
import Bus from './Bus'
//触发事件  可以在methods的逻辑方法中
Bus.$emit('add',200)

用法很简单,用$on去订阅,$emit去触发发布,$emit会回调所有(该名称)用$on注册的回调方法

接下来我们去实现一个简单的Bus

Bus实现代码

//事件中心
class Bus{

//事件队列对象
queue = {};

    //订阅方法
    $on(name,fn){
        //先判断当前注册事件名称是否存在,存在直接等于自身,不存在等于[];
        this.queue[name] = this.queue[name]? this.queue[name] : [];
        //添加到对应数组
        this.queue[name].push(fn)
    }

    //触发发布方法
    $emit(name,...args){
        //如果不存在该订阅数组 直接return
        if(!this.queue[name]){
            return;
        }
        //循环回调函数数组执行回调函数
        this.queue[name].forEach(fn=>{
            fn(...args)
        })
    }

    //取消订阅方法
    $off(name,fn){
        //如果不存在该订阅数组 直接return
        if(!this.queue[name]){
            return;
        }
        //这里相当于做了删除操作  过滤当前的方法fn
        this.queue[name] = this.queue[name].filter(f=>f!==fn)
    }

    //再来一个只被触发一次的方法
    $ones(name,fn){
        //自定义一个回调方法
        const cb = (...args)=>{
            //在回调方法中调用注册时传进来的方法
            fn.call(this,...args);
            //执行完删除当前回调方法,下次回调列表中就没有该回调了
            this.$off(name,cb)
        }
        //先判断当前注册事件名称是否存在,存在直接等于自身,不存在等于[];
        this.queue[name] = this.queue[name]? this.queue[name] : [];
        //添加到对应数组
        this.queue[name].push(cb)
    }
}


好啦, 几个方法都写完了, 下面我们就来测试一下

测试

$on和$emit的使用

    const bus = new Bus();
    //回调函数
    const bF = (v)=>{
      console.log(v)
    }
    //注册事件1
    bus.$on('a',(v)=>{
      console.log(v)
    })
    //注册事件2
    bus.$on('a',bF)
    
    //触发事件
    bus.$emit('a','hello Bus')
    
    //控制台输出两次hello Bus

$off的使用

    const bus = new Bus();
    //回调函数
    const bF = (v)=>{
      console.log(v,'---------')
    }
    //注册事件1
    bus.$on('a',(v)=>{
      console.log(v)
    })
    //注册事件2
    bus.$on('a',bF)
    
    //取消注册事件2的回调方法
    bus.$off('a',bF)
    //触发事件
    bus.$emit('a','hello Bus')
    
    //控制台只输出一次hello Bus

$ones的使用

    const bus = new Bus();
    //回调函数
    const bF = (v)=>{
      console.log(v,'---------')
    }
    //注册事件1
    bus.$on('a',(v)=>{
      console.log(v)
    })
    //注册事件2
    bus.$ones('a',bF)
    
    //触发事件
    bus.$emit('a','hello Bus')
    //再次触发
    bus.$emit('a','hello Bus two')
    
    //注册事件2只被回调一次 输出hello Bus ---------
    //注册事件1被回调2次 输出hello Bus 和 hello Bus two

最后

上面代码都没有做类型判断,如typeof === 'function',理解和会用就好,实际使用可以自己加一下,我又偷个懒,哈哈~

做人和写代码我都不太喜欢复杂的东西,总想着能不能简单点。