设计模式-javascript实现【发布-订阅模式】

发布-订阅模式也叫观察者模式,可以用在异步编程中替代回调函数的方案。发布-订阅模式可以取代对象之间硬
编码的通知机制,一个对象不用显式地调用另一个对象的某个接口。发布-订阅模式让两个对象松耦合地联系在
一起,虽然不太清除彼此的细节,但这不影响它们之间相互通信。

1. 发布-订阅模式的实现思路

  • 指定发布者
  • 给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者
  • 最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数

2. 实现通用发布-订阅模式

创建一个通用的事件发布订阅对象,其他对象可以基于此对象继承其订阅发布功能

// 定义通用的发布订阅对象
const event = {
  clientList: {},
  // 不能使用箭头函数定义方法,this会自动绑定到event对象, 无法指向新创建的对象
  listen: function (key, fn) {
    if(!this.clientList[key]) {
      this.clientList[key] = [];
    }
    // 将订阅消息添加缓存列表
    this.clientList[key].push(fn);
  },
  trigger: function (key, ...payload) {
    const fns  = this.clientList[key];
    if(!fns || fns.length == []) return false;
    fns.forEach((fn) => {
      fn.apply(this, payload);
    });
  }
};

// 通过继承的方式创建发布者
const salesOffice = Object.create(event);
// 订阅发布者的事件
salesOffice.listen('sale80', (price) => {
  console.log(`price is ${price}`);
});
// 发布消息
salesOffice.trigger('sale80', '100w');

3. 创建全局发布-订阅对象

利用全局发布订阅Event对象,我们可以在两个封装良好的模块中进行通信,这两个模块可以完全不知道对方的存在。

class Event {
  constructor(){
    this.clientList = {};
  }
  // 订阅消息
  listen (key, fn) {
    if(!this.clientList[key]) {
      this.clientList[key] = [];
    }
    // 将订阅消息添加缓存列表
    this.clientList[key].push(fn);
    // 返回handler对象,供取消订阅使用
    return {key, fn};
  }

  // 发布事件
  trigger (key, ...payload) {
    const fns  = this.clientList[key];
    if(!fns || fns.length == []) return false;
    fns.forEach((fn) => {
      fn.apply(this, payload);
    });
  }

  // 取消订阅
  remove (handler) {
    const {key, fn} = handler;
    const fns = this.clientList[key];
    if(!fns) return false;
    if(!fn) {
      fns && (fns.length = 0); // 清空事件回调函数
    }
    else {
      for(let [i,value] of fns.entries()) {
        if(value === fn){
          fns.splice(i, 1);
          break;
        }
      }
    }
  }

}
const event = new Event();

// 观察者订阅事件
const handler = event.listen('sale80', (price) => {
  console.log(`price is ${price}`);
});
// 生产者发布消息
event.trigger('sale80', '100w');
// 观察者取消订阅
event.remove(handler); 
event.trigger('sale80', '120w');
posted @ 2023-03-08 15:38  箫笛  阅读(189)  评论(0编辑  收藏  举报