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 就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。
  • 观察者模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式。

最新文章

  1. [php] php图表显示
  2. php 分页类
  3. cf708B. Recover the String---(构造法)
  4. ThinkPHP框架概述
  5. Scala中的类和对象
  6. 随笔001:Group by 语法剪辑
  7. Struts2学习---基本配置,action,动态方法调用,action接收参数
  8. [51nod1355] 斐波那契的最小公倍数
  9. Spark 灰度发布在十万级节点上的成功实践 CI CD
  10. 【代码笔记】Web-Javascript-Javascript typeof
  11. 006_饿了么大前端总监sofish帮你理清前端工程师及大前端团队的成长问题!
  12. HTML和CSS总结
  13. C# 以共享只读方式打开被其它程序占用的文件
  14. [C语言]进阶|结构类型: 枚举, 结构, 类型定义
  15. 初级JS
  16. 遇见CUBA CLI
  17. [转] COM编程总结
  18. Docker入门到实战
  19. SpringBoot 整合 Security5
  20. Anaconda使用、conda的环境管理和包管理

热门文章

  1. SpringCloud升级之路2020.0.x版-12.UnderTow 简介与内部原理
  2. windows的自动登录和隐藏用户
  3. 如何用Git上传项目到GitHub
  4. Shell-13-常用文件目录
  5. ETL需求要求
  6. SQL注入的那些面试题总结
  7. SQL 练习33
  8. docker加速器,设置cdn
  9. C++CLR类库封装Native类库并用C#调用 - 草稿
  10. C#中的垃圾回收