实现发布订阅模式/观察者模式

137 阅读2分钟

发布订阅模式是一个前端开发中非常常用的模式。下面是某大厂一道常见的面试真题,题目真心不难,但要几分钟内快速写出来,却也并不容易。

工作中,有些东西我们从来没有接触过、见闻过,这个时候不OK,是正常的。但更多的情况是,很多的东西不是我们真的就搞不懂,弄不明白,而往往是看过、知道、了解、用过一两次,却因为缺乏反复的刻意练习,从而达不到熟练的程度,做不到运用自如,又或者因为缺乏及时的总结记录,从而时间一长关键收获都已被遗忘或者即使有总结记录也是一点,未能组成体系,从而不OK,这是令人扼腕叹息的。

长此以往,我们似乎知道很多,却似乎又什么也不知道,这是一种非常尴尬又令人痛心的状态。解决办法也似乎不多,就是刻意练习、及时记录总结、找其他人帮助审视和梳理散点串联成体系。

下面,我们就开始写起来吧~

一、实现发布订阅模式

/**
 * 使用JS实现发布订阅模式
 */
type Handler = (params: unknown) => void;

interface Handlers {
  [prop: string]: Array<Handler|null>;
}

export class Event {
  handlers: Handlers = {};

  on(type: string, handler: Handler) {
    if (!this.handlers[type]) {
      this.handlers[type] = [];
    }

    this.handlers[type].push(handler);
  }

  off(type: string, handler: Handler) {
    if (!handler) {
      delete this.handlers[type];
      return;
    }

    if (!this.handlers[type]) {
      throw new Error('未绑定该事件');
    }

    const idx = this.handlers[type].indexOf(handler);
    if (idx === -1) {
      throw new Error('未绑定该处理函数');
    }

    this.handlers[type].splice(idx, 1);
    if (!this.handlers[type].length) {
      delete this.handlers[type];
    }
  }

  emit(type: string, ...params: unknown[]) {
    if (!this.handlers[type] || !this.handlers[type].length) {
      return;
    }

    this.handlers[type].forEach((handler) => {
      handler && handler(params);
    });
  }
}

// const eventBus = new Event();

// const b = (params: unknown) => {
//   console.log('B recieved', params);
// };

// const c = (params: unknown) => {
//   console.log('C recieved', params);
// };

// eventBus.on('publish', b);
// eventBus.on('publish', c);
// eventBus.emit('publish', 'message 1');
// eventBus.off('publish', b);
// eventBus.off('publish', c);
// eventBus.emit('publish', 'message 2');

二、实现观察者模式

/**
 * 使用JS/TS实现观察者模式
 * 例如,a为发布者, b、c为观察者
 */
export class Subject {
  observers: Observer[] = [];

  add(observer: Observer) {
    this.observers.push(observer);
  }

  remove(observer: Observer) {
    const idx = this.observers.indexOf(observer);

    if (idx === -1) {
      throw new Error('没有该observer');
    }

    this.observers.splice(idx, 1);
  }

  notify() {
    this.observers.forEach((observer) => {
      observer.update();
    });
  }
}

export class Observer {
  name: string = '';

  constructor(name: string) {
    this.name = name;
  }

  update() {
    console.log(`${this.name} received`);
  }
}

// const a = new Subject();
// const b = new Observer('B');
// const c = new Observer('C');

// a.add(b);
// a.add(c);
// a.notify();
// a.remove(b);
// a.remove(c);
// a.notify();

代码见这里