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

简介

发布-订阅模式又叫做观察者模式,他定义了一种一对多的依赖关系,即当一个对象的状态发生改变的时候,所有依赖他的对象都会得到通知。

回忆曾经

作为一名前端开发人员,给DOM节点绑定事件可是再频繁不过的事情。比如如下代码

    document.body.addEventListener(‘click‘,function () {
        alert(2333);
    },false);
    document.body.click();//模拟点击事件

这里我们订阅了document.body的click事件,当body被点击的时候,他就向订阅者发布这个消息,弹出2333.我们也可以随意的增加和删除订阅者,当消息一发布,所有的订阅者都会收到消息。

    document.body.addEventListener(‘click‘,function () {
        alert(11111);
    },false);
    document.body.addEventListener(‘click‘,function () {
        alert(222);
    },false);
    document.body.addEventListener(‘click‘,function () {
        alert(333);
    },false);
    document.body.click();//模拟点击事件

值得注意的是,手动触发事件这里我们直接用了document.body.click();但是更好的做法是IE下用fireEvent,标准浏览器下用dispatchEvent,如下:

    let fireEvent = function (element,event) {
        if (document.createEventObject) {
            var evt = document.createEventObject();
            return element.fireEvent(‘on‘+event,evt);
        }else{
            var evt = document.createEvent(‘HTMLEvents‘);
            evt.initEvent(event,true,true);
            return element.dispatchEvent(evt);
        }
    }
    document.addEventListener(‘shout‘,function (event) {
        alert(‘shout‘);
    })
    fireEvent(document,‘shout‘);

畅谈现在

人的日常生活离不开各种人际交涉,比如你的朋友有很多,这时候你要结婚了,要以你为发布者,打开你的通讯录,挨个打电话通知各个订阅者你要结婚的消息。抽象一下,实现发布-订阅模式需要:

  1. 发布者(你)
  2. 缓存列表(通讯录,你的朋友们相当于订阅了你的所有消息)
  3. 发布消息的时候遍历缓存列表,依次触发里面存放的订阅者的回调函数(挨个打电话)
  4. 另外,回调函数中还可以添加很多参数,,订阅者可以接收这些参数,比如你会告诉他们婚礼时间,地点等,订阅者收到消息后可以进行各自的处理。
let yourMsg = {};
yourMsg.peopleList = [];
yourMsg.listen = function (fn) {
    this.peopleList.push(fn);
}
yourMsg.triger = function () {
    for(var i = 0,fn;fn=this.peopleList[i++];){
        fn.apply(this,arguments);
    }
}

yourMsg.listen(function (name) {
    console.log(`${name}收到了你的消息`);
})
yourMsg.listen(function (name) {
    console.log(‘哈哈‘);
})

yourMsg.triger(‘张三‘);
yourMsg.triger(‘李四‘);

  • 以上就是一个简单的发布-订阅的实现,但是我们会发现订阅者会收到发布者发布的每一条信息,如果李四比较阴暗,不想听到你结婚的消息,只想听到你的坏消息,比如你被开除了,他就心里高兴。这时候我们就需要加一个key,让订阅者只订阅自己感兴趣的消息。
let yourMsg = {};
yourMsg.peopleList ={};
yourMsg.listen = function (key,fn) {
    if (!this.peopleList[key]) { //如果没有订阅过此类消息,创建一个缓存列表
        this.peopleList[key] = [];
    }
    this.peopleList[key].push(fn);
}
yourMsg.triger = function () {
    let key = Array.prototype.shift.call(arguments);
    let fns = this.peopleList[key];
    if (!fns || fns.length == 0) {//没有订阅 则返回
        return false;
    }
    for(var i=0,fn;fn=fns[i++];){
        fn.apply(this,arguments);
    }
}

yourMsg.listen(‘marrgie‘,function (name) {
    console.log(`${name}想知道你结婚`);
})
yourMsg.listen(‘unemployment‘,function (name) {
    console.log(`${name}想知道你失业`);
})

yourMsg.triger(‘marrgie‘,‘张三‘);
yourMsg.triger(‘unemployment‘,‘李四‘);

  • 你需要发布消息,同样的所有的人都有朋友圈,也都需要发布消息,因此我们有必要把发布-订阅的功能提取出来,放在一个单独的对象内,谁需要谁去动态安装发布-订阅功能(installEvent函数实现了动态安装发布-订阅功能)。
var event = {
    peopleList:[],
    listen:function (key,fn) {
        if (!this.peopleList[key]) { //如果没有订阅过此类消息,创建一个缓存列表
        this.peopleList[key] = [];
        }
        this.peopleList[key].push(fn)
    },
    trigger:function () {
         let key = Array.prototype.shift.call(arguments);
        let fns = this.peopleList[key];
        if (!fns || fns.length == 0) {//没有订阅 则返回
            return false;
        }
        for(var i=0,fn;fn=fns[i++];){
            fn.apply(this,arguments);
        }
    }
}

var installEvent  = function (obj) {
    for(var i in event){
        obj[i] = event[i];
    }
}

let yourMsg = {};
installEvent(yourMsg);
yourMsg.listen(‘marrgie‘,function (name) {
    console.log(`${name}想知道你结婚`);
})
yourMsg.listen(‘unemployment‘,function (name) {
    console.log(`${name}想知道你失业`);
})

yourMsg.trigger(‘marrgie‘,‘张三‘);
yourMsg.trigger(‘unemployment‘,‘李四‘);
  • 有时间我们需要取消订阅的事件,比如李四是你的好朋友,但是因为一件事情,你俩闹掰了,你把他从你的通讯录中给删除掉了,这里我们给event增加一个remove方法;
remove:function (key,fn) {
      var fns = this.clientList[key];
      if(!fns){
          return false;
      }
      if(!fn){
          fns && (fns.length=0)
      }else{
          for (let index = 0; index < fns.length; index++) {
              const _fn = fns[index];
              if(_fn === fn){
                  fns.splice(index,1);
              }
          }
      }
    }

发布-订阅的顺序探讨

我们通常所看到的都是先订阅再发布,但是必须要遵守这种顺序吗?答案是不一定的。如果发布者先发布一条消息,但是此时还没有订阅者订阅此消息,我们可以不让此消息消失于宇宙之中。就如同QQ离线消息一样,离线的消息被保存在服务器中,接收人下次登录之后,才会收到此消息。同样的,我们可以建立一个存放离线事件的堆栈,当事件发布的时候,如果此时还没有订阅者订阅这个事件,我们暂时把发布事件的动作包裹在一个函数里,这些包装函数会被存入堆栈中,等到有对象来订阅事件的时候,我们将遍历堆栈并依次执行这些包装函数,即重发里面的事件,不过离线事件的生命周期只有一次,就像qq未读消息只会提示你一次一样。

JavaScript实现发布-订阅模式的便利性

因为JavaScript有回调函数这个优势存在,我们写开发-订阅显得更简单一点。传统的发布-订阅比如Java通常会把订阅者自身当成引用传入发布者对象中,同时订阅者对象还需提供一个名为诸如update的方法,供发布者对象在合适的时候调用。下面代码用js模拟下传统的实现。

function Dep() {
    this.subs = [];
}
Dep.prototype.addSub = function (sub) {
    this.subs.push(sub);
}
Dep.prototype.notify = function () {
    this.subs.forEach(sub=>sub.update());
}
function Watcher(fn) {
    this.fn = fn;
}
Watcher.prototype.update = function () {
     this.fn();
}

var dep = new Dep();
dep.addSub(new Watcher(function () {
    console.log(‘okokok‘);
}))
dep.notify();

小结

  • 发布-订阅的优势很明显,做到了时间上的解耦和对象之间的解耦,从架构上看,MVC,MVVM都少不了发布-订阅的参与,我们常用的Vue也是基于发布-订阅的,最近会抽时间写下vue的源码实现,同样的node中的EventEmitter也是发布订阅的,之前也手写过它的实现。
  • 发布-订阅同时也是有缺点存在的,创建订阅者本身要消耗一定的时间和内存,而且当你订阅一个消息以后,可能此消息最后都未发生,但是这个订阅者会始终存在于内存中。如果程序中大量使用发布-订阅的话,也会使得程序跟踪bug变得困难。

原文地址:https://www.cnblogs.com/jlfw/p/11800330.html

时间: 2024-10-18 13:29:55

Javascript设计模式之发布-订阅模式的相关文章

学习javascript设计模式之发布-订阅(观察者)模式

1.发布-订阅模式又叫观察者模式,它定义对象之间一种一对多的依赖关系. 2.如何实现发布-订阅模式 2-1.首先指定好发布者 2-2.给发布者添加一个缓冲列表,用户存放回调函数以便通知订阅者 2-3.最后发布消息时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数 例子: var salesOffice = {};salesOffice.clientList = [];salesOffice.listen = function(key,fn){    if(!this.clientL

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

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

javascript的简易发布/订阅模式

发布/订阅(pub/sub)是一种消息模式,它的两个参与者:发布者和订阅者.发布者向某个信道发布一条消息,订阅者绑定这个信道,当有消息发布到信道里面的时候,订阅者回接受到这个通知.发布者和订阅者是完全解耦的,彼此仅仅共享一个通道名称. 这种模式提高了应用的可维护性,让应用易于扩展. 简单的设计思路:设计信道记录发布的信息,然后在订阅者中执行成功回调函数(从信道中监听事件). var PubSub = { subscribe: function(key, callback) { //this._c

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

观察者模式简单实现,在主题中需要存在一个观察者的集合,存在一个 update(),状态改变的时候遍历调用观察者集合中的对象,调用update()方法实现通知.而观察者需要做的就是重写 update() 实现自己的逻辑,然后订阅主题,接收状态的改变信息. 上面说到的观察者模式 会导致每一个观察者都需要实现同一个方法 update(),这在实际开发中是很难实现的,所以可以通过委托事件的方式 ,在主题的集合中 存放的是一个 event 对象.event 包含对象的信息,调用的方法名,用到的参数等信息.

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

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

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

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

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

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

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

第一部分: 发布订阅模式简介 发布—订阅模式又叫观察者模式,它定义对象间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知.在javascript开发中,一般用事件模型来替代传统的发布—订阅模式. 发布—订阅模式可以广泛应用于异步编程中,是一种替代传递回调函数的方案.比如,可以订阅ajax请求的error.success等事件.或者如果想在动画的每一帧完成之后做一些事情,可以订阅一个事件,然后在动画的每一帧完成之后发布这个事件.在异步编程中使用发布—订阅模式,就无需过

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

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