JavaScript设计模式之----原生JS实现简单的发布订阅模式

第一部分: 发布订阅模式简介

发布—订阅模式又叫观察者模式,它定义对象间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。在javascript开发中,一般用事件模型来替代传统的发布—订阅模式。

发布—订阅模式可以广泛应用于异步编程中,是一种替代传递回调函数的方案。比如,可以订阅ajax请求的error、success等事件。或者如果想在动画的每一帧完成之后做一些事情,可以订阅一个事件,然后在动画的每一帧完成之后发布这个事件。在异步编程中使用发布—订阅模式,就无需过多关注对象在异步运行期间的内部状态,而只需要订阅感兴趣的事件发生点

第二部分:发布订阅模式在DOM编程操作过程中的使用

  发布—订阅模式可以取代对象之间硬编码的通知机制,一个对象不用再显式地调用另外一个对象的某个接口。发布—订阅模式让两个对象松耦合地联系在一起,虽然不太清楚彼此的细节,但这不影响它们之间相互通信。当有新的订阅者出现时,发布者的代码不需要任何修改;同样发布者需要改变时,也不会影响到之前的订阅者。只要之前约定的事件名没有变化,就可以自由地改变它们

实际上,前端开发中常用的,在DOM节点上面绑定事件函数,就属于发布—订阅模式

document.body.addEventListener(‘click‘,function(){
  alert(2);
},false);
document.body.click();

如果需要监控用户点击document.body的动作,但是没办法预知用户将在什么时候点击。所以订阅document.body上的click事件,当body节点被点击时,body节点便会向订阅者发布这个消息。

同理,其实DOM中的很多事件操作都是采用的这个原理

document.body.addEventListener(‘keyup‘,function(){
  alert(2);
},false);
document.body.keyup();

document.body.addEventListener(‘mousedown‘,function(){
  alert(2);
},false);
document.body.mousedown();

还可以为单个事件添加多个监听功能,

document.body.addEventListener(‘click‘,function(){
  console.log(2);
},false);
document.body.addEventListener(‘click‘,function(){
 console.log(3);
},false);
document.body.addEventListener(‘click‘,function(){
  console.log(4);
},false);
document.body.click();    //模拟用户点击

// 2, 3, 4

第三部分:发布订阅模式的其他使用场景

假设有一个店铺,出售Iphone,会有多种型号的Iphone出售,而消费者也会有不同的需求,如果每个消费者都要来向店员询问自己需要的款型的价格,那么这是一个很低效的行为,因为消费者最关心的就是型号和价格,这样用发布订阅模式就最合适不过了

const  shop = {}; // 首先定义一个商铺

shop.list = [];  // 定义商铺里的商品信息列表

shop.listen = function(fn) { // 添加订阅者
    this.list.push(fn); // 将订阅的商品添加进入商品心里列表
}  

shop.sell = function(){
    for( var i = 0, fn; fn = this.list[ i++ ]; ){
        fn.apply( this, arguments ); // (2) // arguments 是发布消息参数
    }
}

// 这是来了一个顾客询问手机的价格,那么
shop.listen(function(iphone, price) {
    console.log(‘手机型号‘ + iphone);
    console.log(‘价格‘ + price)
})

// 发布消息,本店卖IphoneX, 价格7000
shop.sell(‘IphoneX‘, 7000);

shop.sell(‘Iphone11‘, 9000);

// 输出 手机型号IphoneX, 价格7000
// 输出 手机型号Iphone11, 价格9000

现在我们已经实现了一个最简单的发布订阅模式了,但这里还存在一些问题。订阅者接收到了发布者发布的每个消息,如果我只想买Iphone11,我是不关心IphoneX的价格的,但是发布者把IphoneX的信息也推送给了我,这对我来说是不必要的困扰。所以有必要增加一个标示key,让订阅者只订阅自己感兴趣的消息。改写后的代码如下:

const  shop = {}; // 首先定义一个商铺

shop.list = {};  // 定义商铺里的商品信息列表

shop.listen = function(key, fn) { // 添加订阅者
    if ( !this.list[key] ){ // 如果没有订阅,创建一个缓存列表
        this.list[key] = [];
    }
    this.list[key].push( fn ); // 订阅的消息添加进消息缓存列表
}  

shop.sell = function(){
   const key = Array.prototype.shift.call( arguments );// 取出消息
   const fns = this.list[ key ]; // 取出该消息对应的回调函数集合
    if ( !fns || fns.length === 0 ){ // 如果没有订阅该消息,则返回
        return false;
    }
    for( var i = 0, fn; fn = fns[ i++ ]; ){
        fn.apply( this, arguments ); // (2) // arguments 是参数
    }
}

// 这是来了一个顾客询问手机的价格,那么
shop.listen(‘IphoneX‘, function(price) {
    console.log(‘价格‘ + price)
})

// 这是来了一个顾客询问手机的价格,那么
shop.listen(‘Iphone11‘, function(price) {
    console.log(‘价格‘ + price)
})

// 发布消息,本店卖IphoneX, 价格7000
shop.sell(‘IphoneX‘, 7000);

shop.sell(‘Iphone11‘, 9000);

// 输出 价格7000
// 输出 价格9000

依照上面的例子,我们就可以写一个基于对象的发布订阅的模型了

const event = {
    clientList: [],
    listen: function( key, fn ){
        if ( !this.clientList[ key ] ){
            this.clientList[ key ] = [];
        }
            this.clientList[ key ].push( fn ); // 订阅的消息添加进缓存列表
        },
        trigger: function(){
            var key = Array.prototype.shift.call( arguments ), // (1);
            fns = this.clientList[ key ];
            if ( !fns || fns.length === 0 ){ // 如果没有绑定对应的消息
                return false;
            }
            for( var i = 0, fn; fn = fns[ i++ ]; ){
                fn.apply( this, arguments ); // (2) // arguments
            }
        },
        remove: function(key, fn) {
              var fns = this.clientList[ key ];
              if ( !fns ){ // 如果key 被人订阅,则直接返回
                 return false;
              }
              if ( !fn ){ // 如果没有传入具体函数,表示需要取消所有订阅
                 fns && ( fns.length = 0 );
              }else{
                  for ( var l = fns.length - 1; l >=0; l-- ){
                      var _fn = fns[ l ];
                      if ( _fn === fn ){
                         fns.splice( l, 1 ); // 删除订阅者的回调函数
                      }
                  }
             }
        }
    };

// 下面是一个基于class的发布订阅模式的模版,考虑到了边界条件和匿名函数,属于一个比较完整的实现

class Pubsub {
  constructor () {
  }

  list = {};

  // 添加消息监听的方法
  subscribe (topic, func) {
    if (typeof topic !== ‘string‘) {
      throw ‘topic为字符串类型‘
    }
    if (typeof func !== ‘function‘) {
      throw ‘func为函数类型‘
    }
    const list = this.list;
    if (!list[topic]) {
      list[topic] = [];
    }
    list[topic].push(func);    // 为了防止匿名函数的影响,在添加时将取消监听的方法返回
    return () => this.unsubscribe(topic, func);
  }

  // 发布消息的方法
  publish (topic, data) {
    if (typeof topic !== ‘string‘) {
      throw ‘topic必须是字符串类型‘
    }
    const list = this.list;
    if(!list[topic]) {
      throw ‘不存在该事件的监听‘
    } else {
      list[topic].forEach((func)=>{
        func.call(this, data)
      })
    }
  }

  // 移除消息监听的方法
  unsubscribe (topic, func){
    if(typeof topic !== ‘string‘) {
      throw ‘topic为字符串类型‘
    }

    if(func && (typeof func !== ‘function‘)) {
      throw ‘func为函数类型‘
    }
    const list = this.list;
    if(!list[topic]) {
      throw ‘不存在该topic监听‘
    }

    if(!func) { // 如果没有第二个参数,就移除所有的监听事件
      if(list[topic]) {
        delete list[topic]
      }
    } else {
      if(!list[topic].includes(func)) {
        throw ‘要移除的事件不存在‘
      } else {
        const index = list[topic].findIndex(item => item === func);
        list[topic].splice(index, 1);
        if(list[topic].length === 0) {
          delete list[topic]
        }
      }
    }
  }
}

原文地址:https://www.cnblogs.com/liquanjiang/p/11724793.html

时间: 2024-10-25 15:43:29

JavaScript设计模式之----原生JS实现简单的发布订阅模式的相关文章

JavaScript设计模式与开发实践---读书笔记(8) 发布-订阅模式

发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 发布-订阅模式可以广泛应用于异步编程中,这是一种替代传递回调函数的方案. 可以取代对象之间硬编码的通知机制,一个对象不用再显式地调用另外一个对象的某个接口. 自定义事件 首先要指定好谁充当发布者: 然后给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者: 最后发布消息时,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数. 另外,我们还可以往回调函数里填入

js 最简单的发布订阅模式

let _subscriber: any; function autorun(subscriber: Function) { _subscriber = subscriber; _subscriber(); _subscriber = null; } class Observer { #list: Function[] = []; // 订阅者 private get _last() { if (!this.#list.length) return null; return this.#list

Javascript中理解发布--订阅模式

阅读目录 发布订阅模式介绍 如何实现发布--订阅模式? 发布---订阅模式的代码封装 如何取消订阅事件? 全局--发布订阅对象代码封装 理解模块间通信 回到顶部 发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 现实生活中的发布-订阅模式: 比如小红最近在淘宝网上看上一双鞋子,但是呢 联系到卖家后,才发现这双鞋卖光了,但是小红对这双鞋又非常喜欢,所以呢联系卖家,问卖

原生JS实现简单富文本编辑器2

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" con

理解《JavaScript设计模式与开发应用》发布-订阅模式的最终版代码

最近拜读了曾探所著的<JavaScript设计模式与开发应用>一书,在读到发布-订阅模式一章时,作者不仅给出了基本模式的通用版本的发布-订阅模式的代码,最后还做出了扩展,给该模式增加了离线空间功能和命名空间功能,以达到先发布再订阅的功能和防止名称冲突的效果.但是令人感到遗憾的是最终代码并没有给出足够的注释.这让像我一样的小白就感到非常的困惑,于是我将这份最终代码仔细研究了一下,并给出了自己的一些理解,鉴于能力有限,文中观点可能并不完全正确,望看到的大大们不吝赐教,谢谢! 下面是添加了个人注释的

Javascript设计模式之发布-订阅模式

简介 发布-订阅模式又叫做观察者模式,他定义了一种一对多的依赖关系,即当一个对象的状态发生改变的时候,所有依赖他的对象都会得到通知. 回忆曾经 作为一名前端开发人员,给DOM节点绑定事件可是再频繁不过的事情.比如如下代码 document.body.addEventListener('click',function () { alert(2333); },false); document.body.click();//模拟点击事件 这里我们订阅了document.body的click事件,当bo

js 设计模式:观察者和发布订阅模式

总是把这两个当作同一个模式,但其实是不太一样的,现在重温一下. 观察者模式 观察者直接订阅目标,当目标触发事件时,通知观察者进行更新 简单实现 class Observer { constructor(name) { this.name = name; } update() { console.log(`${this.name} update`) } } class subject { constructor() { this.subs = []; } add(observer) { this.

javascript设计模式学习之八_发布订阅模式

一.发布订阅模式定义 发布订阅模式又叫做观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知.发布—订阅模式可以取消对象之间硬编码的通知机制.javascript中的事件机制就属于发布订阅模式的一种. 二.发布订阅模式使用案例 2.1网站登录 假设正在开发一个网站,网站里面有header头部,nav导航,消息列表,购物车等模块,

设计模式-发布订阅模式(javaScript)

1. 前言 2. 什么是发布订阅模式 3. 发布订阅优缺点 4. 举例 4. 总结 1. 前言 发布订阅者模式是为了发布者和订阅者之间避免产生依赖关系,发布订阅者之间的订阅关系由一个中介列表来维护.发布者只需做好发布功能,至于订阅者是谁,订阅者做了什么事情,发布者是无需关心的 2. 什么是发布订阅模式 发布订阅:是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者).而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在.同样的,订阅者可以表达