前端小技巧: TS实现EventBus自定义事件

关于EventBus事件总线

  • 事件总线,实现 on, once, emit, off
    • on, once 是注册函数,存储起来
    • emit时找到对应的函数,执行
    • off找到对应的函数,从对象中删除
  • 注意
    • 区分on和once
    • on绑定的事件可连续执行,除非off
    • once绑定的函数 emit 一次即删除,也可未执行而被 off
    • 数据结构上标识出 on 和 once

实现方案1

代码实现:使用参数区分 on和once

ts 复制代码
class EventBus {
  /*
  {
    key1: [
      {fn: fn1, isOnce: false},
      {fn: fn2, isOnce: false},
      {fn: fn3, isOnce: true},
    ],
    key2: []
  }
  */
 private events: {
  [key: string]: Array<{fn: Function; isOnce: boolean}>
 }
 constructor() {
  this.events = {};
 }

 on(type: string, fn: Function, isOnce: boolean = false) {
  const events = this.events;
  if (events[type] == null) {
    events[type] = []; // 初始化 key 的 fn 数组
  }
  events[type].push({fn, isOnce});
 },
 // 这里是初步版本
 once_origin(type: string, fn: Function) {
  const events = this.events;
  if (events[type] == null) {
    events[type] = []; // 初始化 key 的 fn 数组
  }
  events[type].push({fn, isOnce: true});
 },
 once(type: string, fn: Function, isOnce: boolean = false) {
  this.on(type, fn, true);
 },
 off(type: string, fn?:Function) {
  if (!fn) {
    // 解绑所有 type 的函数
    this.events[type] = [];
  } else {
    // 解绑单个 fn
    const fnList = this.events[type];
    if (fnList.length) {
      this.events[type] = fnList.filter(item.fn !== fn);
    }
  }
 },
 emit(type: string, ...args: any[]) {
  const fnList = this.events[type];
  if (fnList == null) return;
  // 注意
  this.events[type] = fnList.filter(item => {
    const { fn, isOnce } = item;
    fn(...args);
    // 处理once, 它执行一次就要被过滤掉
    if (!isOnce) return true;
    return false;
  })
 }
}

const e = new EventBus();
function fn1(a: any, b: any) {console.log('fn1', a, b)};
function fn2(a: any, b: any) {console.log('fn2', a, b)};
function fn3(a: any, b: any) {console.log('fn3', a, b)};

e.on('key1', fn1);
e.on('key1', fn2);
e.once('key1', fn3);

e.emit('key1', 10, 20); // 触发 fn1, fn2, fn3
e.off('key1', fn1);
e.emit('key1', 100, 200); // 触发 fn2

实现方案2

代码实现: 拆分保存 on和once

js 复制代码
class EventBus {
  private events: { [key: string]: Array<Function>} // {key1: [fn1, fn2]}
  private onceEvents: {[key: string]: Array<Function>} // 结构同上

  constructor() {
    this.events = {}; // 存储 on
    this.onceEvents = {}; // 存储 once
  }
  // on 触发
  on(type: string, fn: Function) {
    const events = this.events;
    if (events[type] === null) events[type] = [];
    events[type].push(fn);
  },
  once(type: string, fn: Function) {
    const onceEvents = this.onceEvents;
    if (onceEvents[type] === null) onceEvents[type] = [];
    onceEvents[type].push(fn);
  },
  // 解绑事件
  off(type: string, fn: Function) {
    if (!fn) {
      // 解绑所有事件
      this.events[type] = [];
      this.onceEvents[type] = [];
    } else {
      // 解绑单个
      const fnList = this.events[type];
      const onceFnList = this.onceEvents[type];
      if (fnList.length) {
        this.events[type] = fnList.filter(curFn => curFN!== fn);
      }
      if (onceFnList.length) {
        this.onceEvents[type] = onceFnList.filter(curFn => curFN!== fn);
      }
    }
  },
  emit(type: string, ...args: any[]) {
    const fnList = this.events[type];
    const onceFnList = this.onceEvents[type];
    if (fnList.length) {
      fnList.forEach(f => f(...args));
    }
    if (onceFnList.length) {
      onceFnList.forEach(f => f(...args));
      // once 执行一次就删除,这里更简单,代码更简洁
      this.onceEvents[type] = [];
    }
  }
}

// 测试用例同上,此处省略

总结

  • 区分 on, once
  • 合理的数据结构,比算法优化更有效
相关推荐
gopher9511几秒前
HTML详解
前端·html
Tiny20171 分钟前
前端模块化CommonJs、ESM、AMD总结
前端
吕永强3 分钟前
CSS相关属性和显示模式
前端·css·css3
结衣结衣.8 分钟前
python中的函数介绍
java·c语言·开发语言·前端·笔记·python·学习
全栈技术负责人9 分钟前
前端提升方向
前端
赵锦川9 分钟前
css三角形:css画箭头向下的三角形
前端·css
qbbmnnnnnn14 分钟前
【WebGis开发 - Cesium】如何确保Cesium场景加载完毕
前端·javascript·vue.js·gis·cesium·webgis·三维可视化开发
f8979070701 小时前
layui动态表格出现 横竖间隔线
前端·javascript·layui
鱼跃鹰飞1 小时前
Leecode热题100-295.数据流中的中位数
java·服务器·开发语言·前端·算法·leetcode·面试
二十雨辰2 小时前
[uni-app]小兔鲜-04推荐+分类+详情
前端·javascript·uni-app