前端小技巧: 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
  • 合理的数据结构,比算法优化更有效
相关推荐
hh随便起个名26 分钟前
useRef和useState对比
前端·javascript·react
Hello_Embed32 分钟前
LVGL 入门(十五):接口优化
前端·笔记·stm32·单片机·嵌入式
huabiangaozhi1 小时前
spring-boot-starter和spring-boot-starter-web的关联
前端
umeelove351 小时前
Spring boot整合quartz方法
java·前端·spring boot
小码哥_常1 小时前
Android 开发探秘:View.post()为何能获取View宽高
前端
爱学习的程序媛1 小时前
【Web前端】WebAssembly详解
前端·web·wasm
不会写DN1 小时前
Js常用的字符串处理
开发语言·前端·javascript
晓13131 小时前
第三章 TypeScript 高级类型
前端·javascript·typescript
一勺菠萝丶1 小时前
芋道项目部署时,前端和门户网站如何通过 Nginx 转发后台接口,而不直接暴露后端地址
运维·前端·nginx
黑白两客2 小时前
Vue 缓存机制
前端·vue.js·缓存