热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaScript实现与使用发布/订阅模式详解

这篇文章主要介绍了JavaScript实现与使用发布订阅模式,较为详细的分析了发布订阅模式的概念、原理并结合实例形式分析了javascript实现与使用发布订阅模式的相关操作技巧,需要的朋友可以参考下

本文实例讲述了Javascript实现与使用发布/订阅模式。分享给大家供大家参考,具体如下:

一、发布/订阅模式简介

发布/订阅模式(即观察者模式): 设计该模式背后的主要动力是促进形成松散耦合。在这种模式中,并不是一个对象调用另一个对象的方法,而是一个订阅者对象订阅发布者对象的特定活动,并在发布者对象的状态发生改变后,订阅者对象获得通知。订阅者也称为观察者,而被观察的对象称为发布者或主题。当发生了一个重要的事件时,发布者将会通知(调用)所有订阅者,并且可能经常以事件对象的形式传递消息。

基本思路:发布者对象需要一个数组类型的属性,以存储所有的订阅者。订阅(即注册)就是将新的订阅者加入到这个数组中去,而注销即是从这个数组中删除某个订阅者。此外,发布消息就是循环遍历订阅者列表并通知他们。

二、如何发布订阅者的方法?

这里我的大体思路是对的。不过当时面试时,我还说了“在发布者之外,还需要定义了一个新的类——订阅者。在订阅者中,需要定义了一个类似 getNews 的方法,以便在发布者发布消息时,调用该方法”。然后,面试官说这样太麻烦了,万一订阅者没有这个方法呢?然后,我不是很懂……

于是我就想到了,在发布消息时直接传递了参数:obj.news = msg; 然后面试官说这样不是更麻烦了吗?这样的话,如果订阅者没有 news 这个属性怎么办?还得判断订阅者是否有 news 这个属性,没有的话就会出现 undifined 的报错。

然后,我就不知道该怎么做了……然后面试官为人特别 nice ,告诉我“可以用继承或者是在注册时候就传入一个 function ”。

面试完后,回家上网查相关知识,整理出的注意点如下:

  1. 发送消息,即通知,意味着调用订阅者对象的某个方法。故当用户订阅信息时,该订阅者需要向发布者的 subscribe() 提供它的其中一个方法——这应该就是面试官所说的注册时候就传入一个方法。
  2. 每个发布者对象需要具有以下成员:

subscribers:一个数组,存储订阅者;
subscribe():注册/订阅,将订阅者添加到 subscribers 数组中;
unsubscribe():取消订阅。从 subscribers 数组中删除订阅者;
publish():循环遍历 subscribers 数组中的每一个元素,并且调用它们注册时所提供的方法;
所有这三种方法都需要一个 type 参数。这是因为发布者可能触发多个事件(比如同时发布一本杂志和一份报纸),而订阅者可能仅选择订阅其中一种,而另外一种不订阅。

三、代码实现

参考《 Javascript 模式》一书,使用字面量实现代码如下:

// 由于这些成员对于任何发布者对象都是通用的,故将它们作为独立对象的一个部分来实现是很有意义的。那样我们可将其复制到任何对象中,并将任意给定对象变成一个发布者。
// 如下实现一个通用发布者,定义发布者对象……
let publisher = {
 subscribers: {
  any: []
 },
 subscribe: function (fn, type = `any`) {
  if (typeof this.subscribers[type] === `undefined`) {
   this.subscribers[type] = [];
  }
  this.subscribers[type].push(fn);
 },
 unSubscribe: function (fn, type = `any`) {
  let newSubscribers = [];
  this.subscribers[type].forEach((item, i) => {
   if (item !== fn) {
    newSubscribers.push(fn);
   }
  });
  this.subscribers[type] = newSubscribers;
 },
 publish: function (args, type = `any`) {
  this.subscribers[type].forEach((item, i) => {
   item(args);
  });
 }
};
// 定义一个函数makePublisher(),它接受一个对象作为参数,通过把上述通用发布者的方法复制到该对象中,从而将其转换为一个发布者
function makePublisher(obj) {
 for (let i in publisher) {
  if (publisher.hasOwnProperty(i) && typeof publisher[i] === `function`) {
   obj[i] = publisher[i];
  }
 }
 obj.subscribers = { any: [] };
}
// 实现paper对象
var paper = {
 daily: function () {
  this.publish(`big news today!`);
 },
 monthly: function () {
  this.publish(`interesting analysis`, `monthly`);
 }
};
// 将paper构造成一个发布者
makePublisher(paper);
// 看看订阅对象joe,该对象有两个方法:
var joe = {
 drinkCoffee: function (paper) {
  console.log(`Just read ` + paper);
 },
 sundayPreNap: function (monthly) {
  console.log(`About to fall asleep reading this ` + monthly);
 }
};
// paper注册joe(即joe向paper订阅)
paper.subscribe(joe.drinkCoffee);
paper.subscribe(joe.sundayPreNap, `monthly`);
// 即joe为默认“any”事件提供了一个可被调用的方法,而另一个可被调用的方法则用于当“monthly”类型的事件发生时的情况。现在让我们来触发一些事件:
paper.daily();   // Just read big news today
paper.daily();   // Just read big news today
paper.monthly();  // About to fall asleep reading this interesting analysis
paper.monthly();  // About to fall asleep reading this interesting analysis
paper.monthly();  // About to fall asleep reading this interesting analysis

我自己又尝试用 ES6 的 class 语法写了一遍(PS:这是转载者自己写的,原文作者是用函数自己又实现了一遍):

// 由于这些成员对于任何发布者对象都是通用的,故将它们作为独立对象的一个部分来实现是很有意义的。那样我们可将其复制到任何对象中,并将任意给定对象变成一个发布者。
// 如下实现一个通用发布者,定义发布者对象……
class Publisher {
 constructor(){
  this.subscribers = {
   any: []
  }
 }
 subscribe(fn, type=`any`){
  if(typeof this.subscribers[type] === `undefined`){
   this.subscribers[type] = [];
  }
  this.subscribers[type].push(fn);
 }
 unSubscribe(fn, type=`any`){
  let newArr = [];
  this.subscribers[type].forEach((item, i) => {
   if(item !== fn){
    newArr.push(fn);
   }
  });
  this.subscribers[type] = newArr;
 }
 publish(args, type=`any`){
  this.subscribers[type].forEach((item, i) => {
   item(args);
  });
 }
 // 定义一个函数makePublisher(),它接受一个对象作为参数,通过把上述通用发布者的方法复制到该对象中,从而将其转换为一个发布者
 static makePublisher(obj){
  obj.publisher = new Publisher();
 }
}
// 实现person对象
var person = {
 sayHi: function(name){
  this.publisher.publish(name);
 },
 sayAge: function(num){
  this.publisher.publish(num, `age`);
 }
}
// 将person构造成一个发布者
Publisher.makePublisher(person);
// 看看订阅对象myLover,该对象有两个方法:
var myLover = {
 name: ``,
 hello: function(name){
  this.name = name;
  console.log(`Hi, i am ` + name + ` ! Nice to meet you!`);
 },
 timeOfLife: function(num){
  console.log(`Hello! My name is ` + this.name + ` ! I am ` + num + ` years old!`);
 }
}
// person注册myLover(即myLover向person订阅)
person.publisher.subscribe(myLover.hello);
person.publisher.subscribe(myLover.timeOfLife, `age`);
// 即myLover为默认“any”事件提供了一个可被调用的方法,而另一个可被调用的方法则用于当“age”类型的事件发生时的情况。现在让我们来触发一些事件:
person.sayHi(`Jimmy`);  // Hi, i am Jimmy ! Nice to meet you!
person.sayAge(24);    // Hello! My name is Jimmy ! I am 24 years old!
person.sayHi(`Tom`);   // Hi, i am Tom ! Nice to meet you!
person.sayAge(6);     // Hello! My name is Tom ! I am 6 years old!
person.sayAge(18);    // Hello! My name is Tom ! I am 18 years old!

更多关于Javascript相关内容还可查看本站专题:《Javascript面向对象入门教程》、《Javascript错误与调试技巧总结》、《Javascript数据结构与算法技巧总结》、《Javascript遍历算法与技巧总结》及《Javascript数学运算用法总结》

希望本文所述对大家Javascript程序设计有所帮助。


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 近年来,大数据成为互联网世界的新宠儿,被列入阿里巴巴、谷歌等公司的战略规划中,也在政府报告中频繁提及。据《大数据人才报告》显示,目前全国大数据人才仅46万,未来3-5年将出现高达150万的人才缺口。根据领英报告,数据剖析人才供应指数最低,且跳槽速度最快。中国商业结合会数据剖析专业委员会统计显示,未来中国基础性数据剖析人才缺口将高达1400万。目前BAT企业中,60%以上的招聘职位都是针对大数据人才的。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • qt学习(六)数据库注册用户的实现方法
    本文介绍了在qt学习中实现数据库注册用户的方法,包括登录按钮按下后出现注册页面、账号可用性判断、密码格式判断、邮箱格式判断等步骤。具体实现过程包括UI设计、数据库的创建和各个模块调用数据内容。 ... [详细]
  • “你永远都不知道明天和‘公司的意外’哪个先来。”疫情期间,这是我们最战战兢兢的心情。但是显然,有些人体会不了。这份行业数据,让笔者“柠檬” ... [详细]
  • 生成对抗式网络GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN介绍
    一、GAN原理介绍学习GAN的第一篇论文当然由是IanGoodfellow于2014年发表的GenerativeAdversarialNetworks(论文下载链接arxiv:[h ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 无线认证设置故障排除方法及注意事项
    本文介绍了解决无线认证设置故障的方法和注意事项,包括检查无线路由器工作状态、关闭手机休眠状态下的网络设置、重启路由器、更改认证类型、恢复出厂设置和手机网络设置等。通过这些方法,可以解决无线认证设置可能出现的问题,确保无线网络正常连接和上网。同时,还提供了一些注意事项,以便用户在进行无线认证设置时能够正确操作。 ... [详细]
  • 本文介绍了游戏开发中的人工智能技术,包括定性行为和非定性行为的分类。定性行为是指特定且可预测的行为,而非定性行为则具有一定程度的不确定性。其中,追逐算法是定性行为的具体实例。 ... [详细]
  • JavaScript设计模式之策略模式(Strategy Pattern)的优势及应用
    本文介绍了JavaScript设计模式之策略模式(Strategy Pattern)的定义和优势,策略模式可以避免代码中的多重判断条件,体现了开放-封闭原则。同时,策略模式的应用可以使系统的算法重复利用,避免复制粘贴。然而,策略模式也会增加策略类的数量,违反最少知识原则,需要了解各种策略类才能更好地应用于业务中。本文还以员工年终奖的计算为例,说明了策略模式的应用场景和实现方式。 ... [详细]
  • 本文介绍了PhysioNet网站提供的生理信号处理工具箱WFDB Toolbox for Matlab的安装和使用方法。通过下载并添加到Matlab路径中或直接在Matlab中输入相关内容,即可完成安装。该工具箱提供了一系列函数,可以方便地处理生理信号数据。详细的安装和使用方法可以参考本文内容。 ... [详细]
  • 本文详细介绍了相机防抖的设置方法和使用技巧,包括索尼防抖设置、VR和Stabilizer档位的选择、机身菜单设置等。同时解释了相机防抖的原理,包括电子防抖和光学防抖的区别,以及它们对画质细节的影响。此外,还提到了一些运动相机的防抖方法,如大疆的Osmo Action的Rock Steady技术。通过本文,你将更好地理解相机防抖的重要性和使用技巧,提高拍摄体验。 ... [详细]
author-avatar
我是小白我有不烦的梦想
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有