《JavaScript设计模式与开发实践》读书笔记。

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

例如:在segmentfault我们关注了某一个问题,这个时候可以说是订阅了这个问题的消息。当该问题有了新的回答、评论的时候,segmentfault系统就会遍历关注了这个问题的用户,一次给用户发消息。

现在看看如何一步步实现发布-订阅模式。

  • 首先,指定好发布者(如 segmentfault 的 问题系统)

  • 接着,给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者(问题系统的记录表)

  • 最后,当发布者发布消息的时候,会遍历缓存列表,依次触发里面的回调函数(遍历记录表,逐个发消息)

我们还可以在回调函数里面加入一些参数,订阅者可以接收这些参数,进行各自的处理。

var sgQuestionSystem = {};    // 定义segmentfault的问题系统

/*
* 缓存列表
* clientList: {
* key: [
* id: <int>, // 唯一标识
* fn: null // 存放回调函数
* ]
* }
*
*/
sgQuestionSystem.clientList = {}; /*
* 添加订阅者(订阅函数),将订阅的类型与回调函数加入缓存列表
* key: 消息的类型
* id: 订阅的唯一标识
* fn: 订阅的回调函数
*/
sgQuestionSystem.listen = function(key, id, fn) {
if(!this.clientList[key]) { // 若缓存列表没有该类型的消息,给该类消息初始化
this.clientList[key] = []
} this.clientList[key].push({ // 将订阅的id, 回调函数添加到对应的消息列表里
id: id,
fn: fn
})
} // 发布消息(发布函数), 依次通知订阅者
sgQuestionSystem.trigger = function () {
var key = Array.prototype.shift.call(arguments), // 取出消息类型
fns = this.clientList[key]; // 取出该消息对应的回调函数集合 if(!fns || fns.length == 0) { // 若订阅列表没有该类型的回到函数,则返回
return false;
} for(var i = 0; i< fns.length; i++) {
fns[i].fn.apply(this, arguments); // arguments是发布消息时附送的参数,去掉了key
}
}

现在,我们来进行一些简单的测试:

// 张三订阅问题A
sgQuestionSystem.listen('questionA', 3, function(questionTitle, content) {
console.log('张三您在早前订阅了问题:questionA');
console.log('现' + questionTitle + '有了新动态');
console.log('内容为:' + content);
}); // 李四订阅问题A
sgQuestionSystem.listen('questionB', 4, function(questionTitle, content) {
console.log('李四您在早前订阅了问题:questionB');
console.log('现' + questionTitle + '有了新动态');
console.log('内容为:' + content);
}) // 问题系统发布消息
sgQuestionSystem.trigger('questionA', '问题A', '王五回答了问题A');
sgQuestionSystem.trigger('questionB', '问题B', '吴六回答了问题B');

至此,我们实现了一个简单的发布-订阅模式,订阅者可以订阅自己感兴趣的事件了。

各位看官,看累了吗?

看累的话点一下收藏,以便您看续集。若您还是精力充沛,那就继续撸吧。


上部分,我们实现了一个问题系统的发布-订阅模式,现在,我们要实现一个文章的发布-订阅模式,这时候,该怎么办?将上面的代码ctrl + c, ctrl + v, 再改下名字?还是有更好的解决方案?

答案显然是有的,我们可以将发布-订阅功能模块提取出来,放在一个单独的对象里面:

var publishSubscribeEvent = {

    /*
* 缓存列表
* clientList: {
* key: [
* id: <int>, // 唯一标识
* fn: null // 存放回调函数
* ]
* }
*
*/
clientList: {}, /*
* 添加订阅者(订阅函数),将订阅的类型与回调函数加入缓存列表
* key: 消息的类型
* id: 订阅的唯一标识
* fn: 订阅的回调函数
*/
listen: function(key, id, fn) {
if(!this.clientList[key]) {
this.clientList[key] = []
} this.clientList[key].push({
id: id,
fn: fn
})
}, // 发布消息(发布函数), 依次通知订阅者
trigger: function () {
var key = Array.prototype.shift.call(arguments),
fns = this.clientList[key]; if(!fns || fns.length == 0) {
return false;
} for(var i = 0; i< fns.length; i++) {
fns[i].fn.apply(this, arguments);
}
}
}

再定义一个安装发布-订阅的函数installPublishSubscribeEvent,这个函数可以给所有对象都动态安装发布-订阅功能:

var installPublishSubscribeEvent = function(obj) {
for(var i in publishSubscribeEvent) {
obj[i] = publishSubscribeEvent[i];
}
}

再来测试一番,我们给文章对象 sgArticleSystem 动态添加发布-订阅功能:

var sgArticleSystem = {};

installPublishSubscribeEvent(sgArticleSystem ); 

// 张三订阅文章A动态
sgArticleSystem.listen('articleA', 3, function(articleTitle, content) {
console.log('张三您在早前订阅了文章:articleA');
console.log('现' + articleTitle+ '有了新动态');
console.log('内容为:' + content);
}); // 李四订阅文章B动态
sgArticleSystem.listen('articleB', 4, function(articleTitle, content) {
console.log('李四您在早前订阅了文章:articleB');
console.log('现' + articleTitle+ '有了新动态');
console.log('内容为:' + content);
}); // 文章系统发布消息
sgArticleSystem.trigger('articleA', 'JavaScript设计模式之发布-订阅模式', '作者修改了文章');
sgArticleSystem.trigger('articleB', 'JavaScript设计模式之策略模式', '王五用户评论了该文章');

好了,该代码经过自测是没有什么问题的,要是各位看官发现问题,欢迎反馈。现在,我们已经可以给我们指定的对象安装发布-订阅模式,但是,是不是还少了点什么功能呢?

答案就是少了取消订阅事件的功能。比如张三突然不想关注该问题的更新动态了,为了避免继续收到问题系统推送过来的消息,张三需要取消之前订阅的事件。现在,我们给 publishSubscribeEvent 对象增加 remove 方法。

publishSubscribeEvent.remove = function(key, id) {
var fns = this.clientList[key]; if(!fns) { // 如果key对应的消息没人订阅,直接返回
return false;
} if(!id) { // 如果没传具体的唯一标识,则取消key的所有对应消息
fns && (fns.length = 0);
} else {
for(var l = fns.length - 1; l >=0; l--) {
var _id = fns[l].id;
if(_id == id) {
fns.splice(l, 1); // 删除订阅者的回调函数
}
}
}
}
// 测试代码
var sgArticleSystem = {}; installPublishSubscribeEvent(sgArticleSystem ); // 张三的订阅
sgArticleSystem.listen('articleA', 3, function(articleTitle, content) {
console.log('张三您在早前订阅了文章:articleA');
console.log('现' + articleTitle+ '有了新动态');
console.log('内容为:' + content);
}); // 李四的订阅
sgArticleSystem.listen('articleA', 4, function(articleTitle, content) {
console.log('李四您在早前订阅了文章:articleA');
console.log('现' + articleTitle+ '有了新动态');
console.log('内容为:' + content);
}); sgArticleSystem.remove('articleA', 3); // 删除张三的订阅
sgArticleSystem.trigger('articleA', 'JavaScript设计模式之发布-订阅模式', '作者修改了文章');

上面的代码跟原著有所不同,原著是在删除订阅的时候是用对比回调函数的,而我是往缓存列表加了一个唯一的标识,用于识别。

[转] JavaScript设计模式之发布-订阅模式(观察者模式)-Part1的更多相关文章

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

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

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

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

  3. javaScript设计模式:发布订阅模式

    发布订阅模式的思想是在观察者模式的基础上演变而来,在观察者模式中客户端监听到对象某个行为就触发对应任务程序.而在发布订阅模式中依然基于这个核心思想,所以有时候也会将两者认为是同一种设计模式.它们的不同 ...

  4. 第五章 --- 关于Javascript 设计模式 之 发布-订阅模式

    先来个最简单的 发布订阅模式 document.body.addEventListener('click',function(){ alert(123); }); document.body.clic ...

  5. JavaScript设计模式_05_发布订阅模式

    发布-订阅模式,定义了对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都将得到通知.发布-订阅模式是使用比较广泛的一种模式,尤其是在异步编程中. /* * pre:发布-订阅 ...

  6. javascript中的发布订阅模式与观察者模式

    这里了解一下JavaScript中的发布订阅模式和观察者模式,观察者模式是24种基础设计模式之一. 设计模式的背景 设计模式并非是软件开发的专业术语,实际上设计模式最早诞生于建筑学. 设计模式的定义是 ...

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

    Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 如何实现发布--订阅模式? 发布---订阅模式的代码封装 如何取消订阅事件? 全局--发布订阅对象代码封装 理解模块间通信 回到 ...

  8. [转] Javascript中理解发布--订阅模式

    发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 现实生活中的发布- ...

  9. 【转】Javascript中理解发布--订阅模式

    Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时 ...

随机推荐

  1. Windows PowerShell 入門(5)-制御構文

    Windows PowerShellにおける制御構文について学びます.数ある制御構文の中でもSwitch文は.他の言語に比べ豊富な機能が用意されています. 対象読者 Windows PowerShel ...

  2. 【HAOI2008】硬币购物

    既然没人写扩欧,那我就来一发吧. 扩欧也还好,就是跑的有点慢,然后写的时候还有点烦,不过还是卡过去了. 考场上看到这道题又蒙了...怎么回事第一题又要爆零了? 然后我打了个暴力测了一下极限数据根本过不 ...

  3. nginx配置文件注释说明

    #定义Nginx运行的用户和用户组 user www www; #nginx进程数,建议设置为等于CPU总核心数. worker_processes 8; #全局错误日志定义类型,[ debug | ...

  4. https 对 json空对象解析的影响

    2017年11月24日09:56:01 记录一个问题: PHP返回json给APP(安卓, fastjson) 其中一个值是空对象  json_encode( [ 'aaa' => new st ...

  5. Android下利用zxing类库实现扫一扫

    程序源代码及可执行文件下载地址:http://files.cnblogs.com/rainboy2010/zxingdemo.zip zxing,一款无比强大的条码解析类库,下面讲解一下如何利用zxi ...

  6. atop工具检测linux硬件异常

    引言 Linux以其稳定性,越来越多地被用作服务器的操作系统(当然,有人会较真地说一句:Linux只是操作系统内核:).但使用了Linux作为底层的操作系统,是否我们就能保证我们的服务做到7*24地稳 ...

  7. python-socket编程(入门,网络基础)

    一.网络基础 网络建立的目的是为了数据交互(通信) 如何实现通信: 1.建立好底层的物理连接介质 2.有一套统一的通信标准,称之为互联网协议 1.osi七层协议 互联网协议按照功能的不同分为osi七层 ...

  8. Modbus库开发笔记之六:Modbus RTU Master开发

    这一节我们来封装最后一种应用(Modbus RTU Master应用),RTU主站的开发与TCP客户端的开发是一致的.同样的我们也不是做具体的应用,而是实现RTU主站的基本功能.我们将RTU主站的功能 ...

  9. SQL Server2008从入门到精通pdf

    下载地址:网盘下载 内容介绍 编辑 <SQL Server 从入门到精通>从初学者的角度出发,通过通俗易懂的语言.丰富多彩的实例,详细地介绍了SQLServer2008开发应该掌握的各方面 ...

  10. Confluence 6 注册单一小工具

    如果你不能订阅一个应用的小工具,你需要将小工具一个一个的添加进来.针对网站不支持小工具订阅和你的应用和你的 Confluence 不能建立信任连接的情况,你就只能这样添加了. 首先你需要获得小工具的 ...