js手写实现eventBus事件总线订阅者发布者模式

eventBus的4个基本事件
on

发布和订阅
发布和订阅。在type上添加订阅者

emit

执行该订阅下的所有函数
执行该订阅下的所有函数。遍历type下的订阅者,执行。
off

取消某个函数的订阅
取消某个函数的订阅。在订阅者中找到fn然后删除
once
只执行一次订阅事件
once方法将handler函数挂载了type这个发布者上,如果执行emit就会执行handler函数中的内容,会先删除type上的所有的函数,然后执行fn。

      // 组件通信,一个触发与监听的过程
      class EventBus {
        constructor() {
          this._events = new Map();//存储发布者和订阅者
        }
        on(type, fn) {//发布和订阅
          //获取到该发布者下的订阅者集合handler
          let handler = this._events.get(type);
          //如果还没有订阅者
          if (!handler) {
            this._events.set(type, [fn]);//加入这个订阅者
          } else {
            handler.push(fn);//直接push进去handler
          }
        }
        emit(type, ...args) {//执行该订阅下的所有函数
          let handler = this._events.get(type);
          for (let i = 0; i < handler.length; i++) {
            handler[i].apply(this, args);//执行第i个订阅者函数
          }
        }
        off(type, fn) {//取消某个函数的订阅
          let handler = this._events.get(type);//获取所有的订阅者
          //数组中订阅者有多个,找到等于fn的订阅者删除
          handler.splice(handler.findIndex(e => e === fn), 1);
        }
        //只执行一次
        once(type, fn) {
          let _self = this;//拿到当前实例对象的this
          function handler() {
            _self.off(type, handler);//删除type下的handler函数
            fn.apply(null, arguments);//执行该函数
          }
          this.on(type, handler)//type下添加handler函数
        }
      }

工作原理在于对事件的监听与手动触发:
作为一个事件总线,它应当具备一个任务队列,以及三个方法:订阅方法、发布方法、取消订阅

class EventBus {
  constructor() {
    this.tasks = {}; // 按事件名称创建任务队列
  }

  /**
   * 注册事件(订阅)
   * @param {String} type  事件名称
   * @param {Function} fn  回调函数
   */
  on(type, fn) {
    // 如果还没有注册过该事件,则创建对应事件的队列
    if (!this.tasks[type]) {
      this.tasks[type] = [];
    }
    // 将回调函数加入队列
    this.tasks[type].push(fn);
  }
  
  /**
   * 注册一个只能执行一次的事件
   * @params type[String] 事件类型
   * @params fn[Function] 回调函数
   */
  once(type, fn) {
    if (!this.tasks[type]) {
      this.tasks[type] = [];
    }

    const that = this;
    // 注意该函数必须是具名函数,因为需要删除,但该名称只在函数内部有效
    function _once(...args) {
      fn(...args);
      that.off(type, _once); // 执行一次后注销
    }

    this.tasks[type].push(_once);
  }

  /**
   * 触发事件(发布)
   * @param {String} type  事件名称
   * @param {...any} args  传入的参数,不限个数
   */
  emit(type, ...args) {
    // 如果该事件没有被注册,则返回
    if (!this.tasks[type]) {
      return;
    }
    // 遍历执行对应的回调数组,并传入参数
    this.tasks[type].forEach((fn) => fn(...args));
  }

  /**
   * 移除指定回调(取消订阅)
   * @param {String} type  事件名称
   * @param {Function} fn  回调函数
   */
  off(type, fn) {
    const tasks = this.tasks[type];
    // 校验事件队列是否存在
    if (!Array.isArray(tasks)) {
      return;
    }

    // 利用 filter 删除队列中的指定函数
    this.tasks[type] = tasks.filter((cb) => fn !== cb);
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值