js设计模式——发布订阅模式

这篇具有很好参考价值的文章主要介绍了js设计模式——发布订阅模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、概述

发布订阅模式是一种常用的设计模式,它定义了一种一对多的关系,让多个订阅者对象同时监听某一个主题对象,当主题对象发生变化时,它会通知所有订阅者对象,使它们能够自动更新 。

二、优缺点

1. 优点:
  • 实现了发布者和订阅者之间的解耦,提高了代码的可维护性和复用性。
  • 支持异步处理,可以实现事件的延迟触发和批量处理。
  • 支持多对多的通信,可以实现广播和组播的功能。
2. 缺点:
  • 可能会造成内存泄漏,如果订阅者对象没有及时取消订阅,就会一直存在于内存中。
  • 可能会导致程序的复杂性增加,如果订阅者对象过多或者依赖关系不清晰,就会增加程序的调试难度。
  • 可能会导致信息的不一致性,如果发布者在通知订阅者之前或之后发生了变化,就会造成数据的不同步。

三、适用场景

发布订阅模式适用于以下场景:

  • 当一个对象的状态变化需要通知其他多个对象时,可以使用发布订阅模式来实现松耦合的通信
  • 当一个事件或消息需要广泛传播或分发给多个接收者时,可以使用发布订阅模式来实现高效的消息分发
  • 当一个系统需要支持异步处理或批量处理时,可以使用发布订阅模式来实现事件的延迟触发或批量触发

四、代码示例

在JavaScript中,实现发布订阅模式的基本思想是:

  • 定义一个发布者对象,它有一个缓存列表,用于存放订阅者对象的回调函数
  • 定义一个订阅方法,用于向缓存列表中添加回调函数
  • 定义一个取消订阅方法,用于从缓存列表中移除回调函数
  • 定义一个发布方法,用于遍历缓存列表,依次执行回调函数,并传递相关参数

下面是一个简单的发布订阅模式的代码示例 :

// 定义一个发布者对象
var pub = {
  // 缓存列表,存放订阅者回调函数
  list: {},
  // 订阅方法
  subscribe: function(key, fn) {
    // 如果没有该消息的缓存列表,就创建一个空数组
    if (!this.list[key]) {
      this.list[key] = [];
    }
    // 将回调函数推入该消息的缓存列表
    this.list[key].push(fn);
  },
  // 取消订阅方法
  unsubscribe: function(key, fn) {
    // 如果有该消息的缓存列表
    if (this.list[key]) {
      // 遍历缓存列表
      for (var i = this.list[key].length - 1; i >= 0; i--) {
        // 如果存在该回调函数,就从缓存列表中删除
        if (this.list[key][i] === fn) {
          this.list[key].splice(i, 1);
        }
      }
    }
  },
  // 发布方法
  publish: function() {
    // 获取消息类型
    var key = Array.prototype.shift.call(arguments);
	// 获取该消息的缓存列表
	var fns = this.list[key];
	// 如果没有订阅该消息,就返回
	if (!fns || fns.length === 0) {
  	return;
	}
	// 遍历缓存列表,执行回调函数
	for (var i = 0; i < fns.length; i++) {
  		fns[i].apply(this, arguments);
	}
  }
};

// 定义一个订阅者对象A 
var subA = function(name) { console.log('A收到了消息:' + name); };
// 定义一个订阅者对象B 
var subB = function(name) { console.log('B收到了消息:' + name); };

// A订阅了test消息 
pub.subscribe('test', subA);
// B订阅了test消息 
pub.subscribe('test', subB);

// 发布了test消息,传递了参数 'hello'
pub.publish('test', 'hello');
// 输出: 
// A收到了消息:hello 
// B收到了消息:hello

// A取消订阅了test消息 
pub.unsubscribe('test', subA);

// 发布了test消息,传递了参数 'world'
pub.publish('test', 'world');
// 输出: // B收到了消息:world

五、 Vue2 响应式系统实现原理

Vue 中,每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

1. 监听器

实现一个方法,这个方法会对需要监听的数据对象进行遍历、给它的属性加上定制的 gettersetter 函数。这样但凡这个对象的某个属性发生了改变,就会触发 setter 函数,进而通知到订阅者。

// observe 方法遍历并包装对象属性
function observe(target) {
  // 若target是一个对象,则遍历它
  if(target && typeof target === 'object') {
    Object.keys(target).forEach((key)=> {
      // defineReactive方法会给目标属性装上“监听器”
      defineReactive(target, key, target[key])
    })
  }
}

// 定义 defineReactive 方法
function defineReactive (obj, key, val) {
  /* 一个 Dep 类对象 */
  const dep = new Dep();
  /* 属性值也可能是 object 类型,这种情况下需要调用 observe 进行递归遍历 */
  observe(val);

  // 为当前属性安装监听器
  Object.defineProperty(obj, key, {
    // 可枚举
    enumerable: true,
    // 不可配置
    configurable: true,
    get: function reactiveGetter () {
      /* 将 Dep.target(即当前的 Watcher 对象存入 dep 的 subs 中) */
      dep.addSub(Dep.target);
      return val;         
    },
    // 监听器函数
    set: function reactiveSetter (newVal) {
      if (newVal === val) return;
      /* 在 set 的时候触发 dep 的 notify 来通知所有的 Watcher 对象更新视图 */
      dep.notify();
    }
  });
}

2. 订阅者
// 定义订阅者类 Dep
class Dep {
  constructor () {
    /* 初始化订阅队列 */
    this.subs = [];
  }

  /* 增加订阅者,在 subs 中添加一个 Watcher 对象 */
  addSub (sub) {
    this.subs.push(sub);
  }

  /* 通知所有 Watcher 对象更新视图 */
  notify () {
    this.subs.forEach((sub) => {
      sub.update();
    })
  }
}

3. 观察者
class Watcher {
  constructor () {
    /* 在 new 一个 Watcher 对象时将该对象赋值给 Dep.target,在 get 中会用到 */
    Dep.target = this;
  }

  /* 更新视图的方法 */
  update () {
    console.log("视图更新啦~");
  }
}

Dep.target = null;
4. Vue 组装
class Vue {
  constructor(options) {
    this._data = options.data;
    observe(this._data);
    /* 新建一个 Watcher 观察者对象,这时候 Dep.target 会指向这个 Watcher 对象 */
    new Watcher();
    /* 在这里模拟 render 的过程,为了触发 test 属性的 get 函数 */
    console.log('render~', this._data.test);
  }
}
let vm = new Vue({
  data:{
    test:"origin"
  }
});

vm._data.test="测试更改!";

/* 运行结果
 * render~ origin
 * 视图更新啦~
 * "测试更改!"
*/

六、实现一个 Event Bus / Event Emitter

Event Bus / Event Emitter 作为全局事件总线,它起到的是一个沟通桥梁的作用。我们可以把它理解为一个事件中心,我们所有事件的订阅/发布都不能由订阅方和发布方“私下沟通”,必须要委托这个事件中心帮我们实现。

class EventEmitter {
  constructor() {
    // handlers是一个map,用于存储事件与回调之间的对应关系
    this.handlers = {}
  }

  // on方法用于安装事件监听器,它接受目标事件名和回调函数作为参数
  on(eventName, cb) {
    // 先检查一下目标事件名有没有对应的监听函数队列
    if (!this.handlers[eventName]) {
      // 如果没有,那么首先初始化一个监听函数队列
      this.handlers[eventName] = []
    }

    // 把回调函数推入目标事件的监听函数队列里去
    this.handlers[eventName].push(cb)
  }

  // emit方法用于触发目标事件,它接受事件名和监听函数入参作为参数
  emit(eventName, ...args) {
    // 检查目标事件是否有监听函数队列
    if (this.handlers[eventName]) {
      // 这里需要对 this.handlers[eventName] 做一次浅拷贝,主要目的是为了避免通过 once 安装的监听器在移除的过程中出现顺序问题
      const handlers = this.handlers[eventName].slice()
      // 如果有,则逐个调用队列里的回调函数
      handlers.forEach((callback) => {
        callback(...args)
      })
    }
  }

  // 移除某个事件回调队列里的指定回调函数
  off(eventName, cb) {
    const callbacks = this.handlers[eventName]
    const index = callbacks.indexOf(cb)
    if (index !== -1) {
      callbacks.splice(index, 1)
    }
  }

  // 为事件注册单次监听器
  once(eventName, cb) {
    // 对回调函数进行包装,使其执行完毕自动被移除
    const wrapper = (...args) => {
      cb(...args)
      this.off(eventName, wrapper)
    }
    this.on(eventName, wrapper)
  }
}


// 使用方法
const eventBus = new EventEmitter()
eventBus.on('test', (val) => {
    console.log(val, "===test")
})
eventBus.emit('test', 21) // 输出: 21,===test

总结

发布订阅模式是一种常用的设计模式,它可以实现对象间的松耦合通信,支持异步处理和多对多的通信。它也有一些缺点,比如可能会造成内存泄漏、程序复杂性增加和信息不一致性。在使用发布订阅模式时,需要注意合理地设计发布者和订阅者之间的关系,避免出现不必要的问题。文章来源地址https://www.toymoban.com/news/detail-742208.html

到了这里,关于js设计模式——发布订阅模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 设计模式之订阅发布模式

    订阅发布模式(Publish-Subscribe Pattern)是一种行之有效的解耦框架与业务逻辑的方式,也是一种常见的观察者设计模式,它被广泛应用于事件驱动架构中。 在这个模式中,发布者(或者说是主题)并不直接发送消息给订阅者,而是通过调度中心(或者叫消息代理)来传递消息

    2024年02月06日
    浏览(32)
  • 设计模式之观察者(发布订阅)模式

    设计模式之观察者(发布订阅)模式

    观察者模式定义了一种一对多的依赖关系,让多个观察者对象同事监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己 观察者模式主要解决的问题: 当一个对象状态发生改变后给其他的对象通知 观察者的优点: 观察者和

    2024年02月07日
    浏览(39)
  • 设计模式(四) —— 观察者模式/发布订阅模式,c和c++示例代码

    往期地址: 设计模式(一)——简单工厂模式 设计模式(二)——策略模式 设计模式(三)——装饰模式 本期主题: 使用c和c++代码,讲解观察者模式、发布订阅模式 发布-订阅模式是一种行为设计模式,它允许多个对象通过事件的发布和订阅来进行通信; 在这种模式中,

    2023年04月17日
    浏览(42)
  • 【JavaScript】手撕前端面试题:寄生组合式继承 | 发布订阅模式 | 观察者模式

    🧑‍💼个人简介:大三学生,一个不甘平庸的平凡人🍬 🖥️ NodeJS专栏:Node.js从入门到精通 🖥️ 博主的前端之路(源创征文一等奖作品):前端之行,任重道远(来自大三学长的万字自述) 🖥️ TypeScript知识总结:TypeScript从入门到精通(十万字超详细知识点总结) 👉

    2023年04月08日
    浏览(44)
  • 前端设计模式:工厂方法模式、单例模式、订阅模式、中介者模式

    工厂方法模式是一种创建型设计模式,它提供了一种将对象的创建与使用分离的方式。在工厂方法模式中,我们定义一个工厂接口,该接口声明了一个用于创建对象的方法。具体的对象创建则由实现该接口的具体工厂类来完成。 工厂方法模式的核心思想是将对象的创建延迟到

    2024年02月12日
    浏览(37)
  • RabbitMQ学习——发布订阅/fanout模式 & topic模式 & rabbitmq回调确认 & 延迟队列(死信)设计

    RabbitMQ学习——发布订阅/fanout模式 & topic模式 & rabbitmq回调确认 & 延迟队列(死信)设计

    1.rabbitmq队列方式的梳理,点对点,一对多; 2.发布订阅模式,交换机到消费者,以邮箱和手机验证码为例; 3.topic模式,根据规则决定发送给哪个队列; 4.rabbitmq回调确认,setConfirmCallback和setReturnsCallback; 5.死信队列,延迟队列,创建方法,正常—死信,设置延迟时间; 点对

    2024年02月13日
    浏览(40)
  • RabbitMQ基础(2)——发布订阅/fanout模式 & topic模式 & rabbitmq回调确认 & 延迟队列(死信)设计

    RabbitMQ基础(2)——发布订阅/fanout模式 & topic模式 & rabbitmq回调确认 & 延迟队列(死信)设计

    1.rabbitmq队列方式的梳理,点对点,一对多; 2.发布订阅模式,交换机到消费者,以邮箱和手机验证码为例; 3.topic模式,根据规则决定发送给哪个队列; 4.rabbitmq回调确认,setConfirmCallback和setReturnsCallback; 5.死信队列,延迟队列,创建方法,正常—死信,设置延迟时间; 点对

    2024年02月10日
    浏览(38)
  • 4.设计模式之后七种模式后11种模式命令访问者迭代器发布订阅中介者忘备录解释器状态策略职责链和空模式

    4.设计模式之后七种模式后11种模式命令访问者迭代器发布订阅中介者忘备录解释器状态策略职责链和空模式

    1.命令(command)模式 不知道命令接收者(对象)是谁,支持撤销 (接受者 间接调用执行 的具体行为) 命令调用者和接收者解耦 //只要实现命令接口即可 (就是客户端给个命令,然后命令类传给接收类执行) 优点和缺点 容易撤销操作 命令队列可以多线程操作 增加过多的命令类 空命令也

    2024年02月12日
    浏览(43)
  • 【前端知识】JavaScript——设计模式(工厂模式、构造函数模式、原型模式)

    工厂模式是一种众所周知的设计模式,广泛应用于软件工程领域,用于抽象创建特定对象的过程。 优点:可以解决创建多个类似对象的问题 缺点:没有解决对象标识问题(即新创建的对象是什么类型) 示例: 构造函数模式与工厂模式相比,没有显式地创建对象,其属性和方

    2024年02月15日
    浏览(23)
  • JavaScript 简单实现观察者模式和发布-订阅模式

    JavaScript 简单实现观察者模式和发布-订阅模式

    大家好,我是南木元元,热衷分享有趣实用的文章。今天来聊聊设计模式中常用的观察者模式和发布-订阅模式。 观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。 如何理解这句话呢?来举个生活中的例子

    2024年02月12日
    浏览(260)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包