发布订阅模式与简单的 JS 实现

发布订阅模式与简单的 JS 实现

本文写于 2020 年 9 月 22 日

观察者模式(Observer Pattern)是一种设计模式,也可以叫做「发布-订阅模式」。

等等,其实我也不清楚,通常你在网上会看到上面这一段话,但我在 Angular 文档中读到谷歌的说法是这样的:观察者模式和发布/订阅模式非常相似(但不完全一样)。

好吧,我们姑且就直说发布订阅模式吧。

发布订阅模式就像你订了一本杂志,每当杂志新刊发布的时候,所有订阅过杂志的人都会收到新刊一样。

观察者模式定义了对象之间一种 一对多 的依赖关系,每当该对象的状态发生改变的时候,所依赖于它的其他对象都将得到通知。

在 JavaScript 中,我们会经常接触到一个很类似的东西——事件。

事件与观察者模式

我们先为一个 #app 元素添加两个点击事件:

document.querySelector('#app').addEventListener('click', () => {
  // 事件 1
});
document.querySelector('#app').addEventListener('click', () => {
  // 事件 2
});

这个时候,我们点击 #app 元素或者使用 document.querySelector('#app').click() 或者 document.querySelector('#app').dispatchEvent('click') 来触发时,这两个事件都会被触发——是不是类似与很多对象依赖于一个对象,该对象改变后所有依赖者都会被触发。

自定义「发布-订阅」

我们来自定义一个事件来试试。

首先我们需要一个数组储存所有的触发函数,这样我们就可以对其进行遍历,后触发每一个事件。

const children = [];

我们还需要一个方法,来对 children 数组进行 push

const listen = (fn) => {
  children.push(fn);
}

我们再增加一个触发函数 trigger,让它去遍历并触发我们的 children

const trigger = () => {
  children.forEach((child) => {
    child();
  })
}

OK,接下来我们整合一下:

class eventHub {
  children = {}

  listen(key, fn) {
    if(this.children[key]) {
      this.children[key].push(fn);
    } else {
      this.children[key] = [fn]
    }
  }

  trigger(key) {
    this.children[key]?.forEach((child) => {
      child();
    });
  }

  remove(key, fn) {
    if(this.children[key]) {
      const children =this.children[key]
      const target = children.indexOf(fn)
      this.children[key] = [...children.slice(0, target), ...children.slice(target + 1)]
    }
  }
}

这里我们将数组换成了对象,并且为函数增加了参数,因为我们希望够可以对多个事件进行监听。

还添加了一个移除事件——所以我们最好使用具名函数,如果是匿名函数就无法移除监听了!

有哪些例子?

例如一个电商网站的登陆系统,需要获取用户基础信息后,获取用户的各项历史记录:

function onLogin() {
  getUserInfo()
    .then(() => {
      return getUserHistory()
    })
    .then(() => {
      getUserXXX()
    })
}

那么一旦我们需要新增一个功能,就需要在这个长函数中增加新项。

如果使用了发布-订阅模式,就可以非常简单的写上新的、独立的事件监听了。

(完)

posted @ 2020-09-22 08:40  徐航宇  阅读(709)  评论(0编辑  收藏  举报