JS手撕(七) 事件总线

2023-01-01 16:20:39 浏览数 (1)

JS手撕(七)    事件总线

事件总线

事件总线是什么呢? 事件总线其实就是发布订阅模式的一种实现。

学习JS的话,就一定会接触到事件的概念。比如给一个按钮绑定点击事件,绑定事件后,点击按钮会触发回调函数。

用发布订阅的说法来讲就是:给按钮绑定点击事件就是让按钮订阅点击事件,点击按钮就会发布事件,就会触发绑定事件时的回调函数。

实现

开始写之前,先需要分析一下解题思路,方便后面一马平川(假)。

基础结构:

代码语言:javascript复制
class EventBus {
  constructor() {
    this.callbacks = { };
  }
}

上面的callbacks就是用来存储所有的订阅事件的回调函数的。这里使用对象的形式而不是使用数组,是因为一个事件应该可以有多个回调,即该对象的键是事件名称,值是事件对应的回调函数数组。

订阅事件

订阅事件实现原理就是:会先判断有没有该对象的回调。如果有就会通过push方法来添加新的回调,没有则赋值为数组再添加回调。

如果都直接使用push方法的话,因为第一次添加回调的时候,该事件还没有回调,所以此时的值是undefined,而不是数组,调用push方法的时候会报错。

如果都不使用push方法,而是直接赋值的话,就会导致一个事件只能有一个回调。

代码语言:javascript复制
on(eventName, callback) {
  if (!this.callbacks[eventName]) {
    // 如果是第一次给该事件添加回调,需要赋值为空数组
    this.callbacks[eventName] = [];
  }

  this.callbacks[eventName].push(callback);
}

代码语言:javascript复制
const eventbus = new EventBus();

eventbus.on('login', (name) => {
  console.log(`${name}登录了!`);
})

eventbus.on('login', (name) => {
  console.log(`bug,${name}连续登录了!`);
})

console.log(eventbus.callbacks);

发布事件

发布事件就是取出对应事件的回调数组,然后依次执行回调。需要判断有没有回调函数,以及回调数组是不是空数组。

突然发现设计模式也不都是很难的(之前看设计模式时,疯狂催眠,完全看不进去,现在实操发现还行)

代码语言:javascript复制
emit(eventName, data) {
  const callbacks = this.callbacks[eventName];

  if (callbacks && callbacks.length > 0) {
    callbacks.forEach(callback => {
      callback(data);
    })
  } else {
    throw new Error('没有订阅该事件或事件回调为空');
  }
}

代码语言:javascript复制
const eventbus = new EventBus();

eventbus.on('login', (name) => {
  console.log(`${name}登录了!`);
})

eventbus.on('login', (name) => {
  console.log(`bug,${name}连续登录了!`);
})

eventbus.on('logout', (name) => {
  console.log(`${name}退出登录了`);
})

eventbus.emit('login', '赤蓝紫');

eventbus.emit('logout', '赤蓝紫');
eventbus.emit('logout', '赤蓝紫');

eventbus.emit('buy', '123');

取消订阅

取消订阅分为两种情况:没有传,则清空所有的事件。传了事件名,删除该事件的全部回调。(不考虑传回调函数,清除指定事件回调的情况)

代码语言:javascript复制
off(eventName) {
  if (typeof eventName === 'undefined') {
    this.callbacks = {};
    return;
  }

  if (this.callbacks[eventName]) {
    delete this.callbacks[eventName];
  } else {
    throw new Error('没有订阅该事件');
  }
}

代码语言:javascript复制
const eventbus = new EventBus();

eventbus.on('login', (name) => {
  console.log(`${name}登录了!`);
})

eventbus.on('logout', (name) => {
  console.log(`${name}退出登录了!`)
});


eventbus.emit('login', '赤蓝紫');
eventbus.off('login');

// 取消`login`事件的订阅后,还能发布`logout`事件,但不能发布`login`事件
eventbus.emit('logout', '赤蓝紫');
eventbus.emit('login', '赤蓝紫');

完整代码

代码语言:javascript复制
class EventBus {
  constructor() {
    this.callbacks = {};
  }

  on(eventName, callback) {
    if (!this.callbacks[eventName]) {
      // 如果是第一次给该事件添加回调,需要赋值为空数组
      this.callbacks[eventName] = [];
    }

    this.callbacks[eventName].push(callback);
  }

  emit(eventName, data) {
    const callbacks = this.callbacks[eventName];

    if (callbacks && callbacks.length > 0) {
      callbacks.forEach(callback => {
        callback(data);
      })
    } else {
      throw new Error('没有订阅该事件或事件回调为空');
    }
  }

  off(eventName) {
    if (typeof eventName === 'undefined') {
      this.callbacks = {};
      return;
    }

    if (this.callbacks[eventName]) {
      delete this.callbacks[eventName];
    } else {
      throw new Error('没有订阅该事件');
    }
  }

}

0 人点赞