前端小技巧: 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
  • 合理的数据结构,比算法优化更有效
相关推荐
折翼的恶魔5 小时前
前端学习之样式设计
前端·css·学习
IT_陈寒5 小时前
JavaScript性能优化:3个被低估的V8引擎技巧让你的代码提速50%
前端·人工智能·后端
云飞云共享云桌面6 小时前
SolidWorks服务器多人使用方案
大数据·运维·服务器·前端·网络·电脑·制造
艾小码6 小时前
从Hello World到变量数据类型:JavaScript新手避坑指南
前端·javascript
街尾杂货店&7 小时前
css word-spacing属性
前端·css
千叶寻-7 小时前
正则表达式
前端·javascript·后端·架构·正则表达式·node.js
光影少年12 小时前
angular生态及学习路线
前端·学习·angular.js
无尽夏_14 小时前
HTML5(前端基础)
前端·html·html5
Jagger_14 小时前
敏捷开发流程-精简版
前端·后端
FIN666815 小时前
昂瑞微冲刺科创板:创新驱动,引领射频芯片国产化新征程
前端·安全·前端框架·信息与通信·芯片