1. 定义

发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。

订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。

2 发布订阅模式的图解以及与观察者模式之间的差异和类似之处:

3 实现发布订阅模式的 相关思路:

  • 创建一个对象
  • 在该对象上创建一个缓存列表(调度中心)
  • on 方法用来把函数 fn 都加到缓存列表中(订阅者注册事件到调度中心)
  • emit 方法取到 arguments 里第一个当做 event,根据 event 值去执行对应缓存列表中的函数(发布者发布事件到调度中心,调度中心处理代码)
  • off 方法可以根据 event 值取消订阅(取消订阅)
  • once 方法只监听一次,调用完毕后删除缓存函数(订阅一次)

eg1:简单一点的demo:

// 定义一个对象作为调度中心,或者一个类。
let eventEmitter = {}; // 缓存列表,存放 event 及 fn
eventEmitter.list = {}; // 订阅
eventEmitter.on = function (event, fn) {
let _this = this;
// 如果对象中没有对应的 event 值,也就是说明没有订阅过,就给 event 创建个缓存列表
// 如有对象中有相应的 event 值,把 fn 添加到对应 event 的缓存列表里
(_this.list[event] || (_this.list[event] = [])).push(fn);
return _this;
}; // 发布
eventEmitter.emit = function () {
let _this = this;
// 第一个参数是对应的 event 值,直接用数组的 shift 方法取出
let event = [].shift.call(arguments), // 删除并拿到arguments的第一项 详细参考:https://blog.csdn.net/u013946061/article/details/108269650博客。
fns = [..._this.list[event]];
// 如果缓存列表里没有 fn 就返回 false
if (!fns || fns.length === 0) {
return false;
}
// 遍历 event 值对应的缓存列表,依次执行 fn
fns.forEach(fn => {
fn.apply(_this, arguments);
});
return _this;
}; function user1 (content) {
console.log('用户1订阅了:', content);
}; function user2 (content) {
console.log('用户2订阅了:', content);
}; // 订阅
eventEmitter.on('article', user1);
eventEmitter.on('article', user2); // 发布
eventEmitter.emit('article', 'Javascript 发布-订阅模式'); /*
用户1订阅了: Javascript 发布-订阅模式
用户2订阅了: Javascript 发布-订阅模式
*/e

eg2: 实现一次订阅的once方法和取消订阅的off方法:

let eventEmitter = {
// 缓存列表
list: {},
// 订阅
on (event, fn) {
let _this = this;
// 如果对象中没有对应的 event 值,也就是说明没有订阅过,就给 event 创建个缓存列表
// 如有对象中有相应的 event 值,把 fn 添加到对应 event 的缓存列表里
(_this.list[event] || (_this.list[event] = [])).push(fn);
return _this;
},
// 监听一次 并且只执行一次 之后取消订阅
once (event, fn) {
// 先绑定,调用后删除
let _this = this;
function on () {
_this.off(event, on); //取消订阅
fn.apply(_this, arguments); // 执行fn函数
}
on.fn = fn;
_this.on(event, on);
return _this;
},
// 取消订阅
off (event, fn) {
let _this = this;
let fns = _this.list[event];
// 如果缓存列表中没有相应的 fn,返回false
if (!fns) return false;
if (!fn) {
// 如果没有传 fn 的话,就会将 event 值对应缓存列表中的 fn 都清空
fns && (fns.length = 0);
} else {
// 若有 fn,遍历缓存列表,看看传入的 fn 与哪个函数相同,如果相同就直接从缓存列表中删掉即可
let cb;
for (let i = 0, cbLen = fns.length; i < cbLen; i++) {
cb = fns[i];
if (cb === fn || cb.fn === fn) {
fns.splice(i, 1);
break
}
}
}
return _this;
},
// 发布
emit () {
let _this = this;
// 第一个参数是对应的 event 值,直接用数组的 shift 方法取出
let event = [].shift.call(arguments),
fns = [..._this.list[event]];
// 如果缓存列表里没有 fn 就返回 false
if (!fns || fns.length === 0) {
return false;
}
// 遍历 event 值对应的缓存列表,依次执行 fn
fns.forEach(fn => {
fn.apply(_this, arguments);
});
return _this;
}
}; function user1 (content) {
console.log('用户1订阅了:', content);
} function user2 (content) {
console.log('用户2订阅了:', content);
} function user3 (content) {
console.log('用户3订阅了:', content);
} function user4 (content) {
console.log('用户4订阅了:', content);
} // 订阅
eventEmitter.on('article1', user1);
eventEmitter.on('article1', user2);
eventEmitter.on('article1', user3); // 取消user2方法的订阅
eventEmitter.off('article1', user2); eventEmitter.once('article2', user4) // 发布
eventEmitter.emit('article1', 'Javascript 发布-订阅模式');
eventEmitter.emit('article1', 'Javascript 发布-订阅模式');
eventEmitter.emit('article2', 'Javascript 观察者模式');
eventEmitter.emit('article2', 'Javascript 观察者模式'); // eventEmitter.on('article1', user3).emit('article1', 'test111'); /*
用户1订阅了: Javascript 发布-订阅模式
用户3订阅了: Javascript 发布-订阅模式
用户1订阅了: Javascript 发布-订阅模式
用户3订阅了: Javascript 发布-订阅模式
用户4订阅了: Javascript 观察者模式
*/

  1 优点:

  • 对象之间解耦
  • 异步编程中,可以更松耦合的代码编写

  2 不足之处:

  • 创建订阅者本身要消耗一定的时间和内存
  • 虽然可以弱化对象之间的联系,多个发布者和订阅者嵌套一起的时候,程序难以跟踪维护

观察者模式和发布订阅模式之间的区别和异同:

观察者模式:观察者(Observer)直接订阅(Subscribe)主题(Subject),而当主题被激活的时候,会触发(Fire Event)观察者里的事件。

发布订阅模式:订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。

差异:

  • 在观察者模式中,观察者是知道 Subject 的,Subject 一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
  • 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
  • 观察者模式大多数时候是同步的,比如当事件触发,Subject 就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。
  • 观察者模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式。

js设计模式之发布订阅模式的更多相关文章

  1. js设计模式之发布/订阅模式模式

    一.前言 发布订阅模式,基于一个主题/事件通道,希望接收通知的对象(称为subscriber)通过自定义事件订阅主题,被激活事件的对象(称为publisher)通过发布主题事件的方式被通知. 就和用户 ...

  2. 浅谈js设计模式之发布 — 订阅模式

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

  3. [转] JavaScript设计模式之发布-订阅模式(观察者模式)-Part1

    <JavaScript设计模式与开发实践>读书笔记. 发布-订阅模式又叫观察者模式,它定义了对象之间的一种一对多的依赖关系.当一个对象的状态发生改变时,所有依赖它的对象都将得到通知. 例如 ...

  4. JS 设计模式八 -- 发布订阅者模式

    概念 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多(一个发布,多个观察)的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 优点 1 ...

  5. JS中的发布订阅模式

    一. 你是如何理解发布订阅模式的 JS中的设计模式: 单例模式:处理业务逻辑 构造原型模式:封装类库,组件,框架,插件等 类库:jQuery 只是提供了一些常用的方法,可以应用到任何的项目中,不具备业 ...

  6. JavaScript设计模式(发布订阅模式)

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

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

    简介 发布-订阅模式又叫做观察者模式,他定义了一种一对多的依赖关系,即当一个对象的状态发生改变的时候,所有依赖他的对象都会得到通知. 回忆曾经 作为一名前端开发人员,给DOM节点绑定事件可是再频繁不过 ...

  8. javascript中的设计模式之发布-订阅模式

    一.定义 又叫观察者模式,他定义对象间的依照那个一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将的到通知.在javascript中,我们一般用时间模型来替代传统的发布-订阅模式 二 ...

  9. js里的发布订阅模式及vue里的事件订阅实现

    发布订阅模式(观察者模式) 发布订阅模式的定义:它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 发布订阅模式在JS中最常见的就是DOM的事件绑定与触发 ...

随机推荐

  1. 关于const声明一些东西

    const int a;        int const a;        const int *a;     int *const a;    const int *const a;   前两个 ...

  2. 暴力破解 安鸾 Writeup

    前三题可以使用hydra进行破解 hydra使用教程 https://www.cnblogs.com/zhaijiahui/p/8371336.html D:\soft\hydra-windows&g ...

  3. SQL 练习34

    求每门课程的学生人数 SELECT cid,COUNT(cid) 课程人数 from sc GROUP BY cid

  4. docker安装maven私服

    目录 一.nexus3安装 二.创建私服仓库 三.发布jar包到私服 四.引用maven私服jar包 五.参考 一.nexus3安装 1.安装镜像 docker pull sonatype/nexus ...

  5. redis缓存穿透,缓存击穿,缓存雪崩

    缓存穿透 缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有.这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询).这样请求就会绕过 ...

  6. C#比较两个对象是否为同一个对象。 Visual Studio调试器指南---多线程应用程序调试(一)

    两个对象是否为同一个对象:是看两个对象是否指向堆中的同一块内存. 1.使用object.ReferenceEquals() class Program { static void Main(strin ...

  7. WPF---数据绑定之ItemsControl(三)

    一.Combox绑定 场景:定义多个Person,Person有Name和Age属性,将多个Person与Combox进行绑定,Combox中只显示Name信息,点击任意一个item,在左侧显示该条目 ...

  8. centos7 添加磁盘到/(根目录下),扩展VG卷和lv

    转载自:http://blog.itpub.net/25469263/viewspace-2662215/ centos7中使用vg方式扩充root分区 1.创建pv fdisk /dev/sdb 输 ...

  9. div 居中显示

    <html lang="en"> <head> <meta charset="UTF-8"> <title>di ...

  10. MySQL 常用的聚合函数

    [常用的聚合函数] mysql聚合函数一般用户统计一列值进行计算,然后返回计算结果.一般于分组group by 配合使用. count //统计个数 select count(*) from test ...