前端小技巧: 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
  • 合理的数据结构,比算法优化更有效
相关推荐
Csvn7 小时前
WASM
前端
修己xj9 小时前
我用 AI 给自己写了一个 Markdown 编辑器,然后把它开源了
前端
FRing039 小时前
vue2干货总结2(v-bind、v-for、v-model、指令修饰符、v-bind操作class和style)
开发语言·前端·vue.js·vue2
幸运小猿9 小时前
模拟慢上传-处理超时重试image 丢失线上问题
前端·javascript·html
moshuying11 小时前
网购能买Token了!
前端
h_654321011 小时前
echarts实现柱状图加一条横线
前端·echarts
风骏时光牛马11 小时前
生成一个后端标题,只需要一个标题,不需要其他的任何文本。
前端
子非鱼@Itfuture11 小时前
关于前后端联调中登录状态/登录信息失效?
java·前端
郭小李11 小时前
openFeign入门
java·开发语言·前端
馒头不想说话11 小时前
AI当运营,帮你省人力
前端