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

 更新时间:2019年01月19日 09:23:20   作者:沐风的心  
这篇文章主要介绍了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程序设计有所帮助。

相关文章

  • jQuery与JavaScript节点创建方法的对比

    jQuery与JavaScript节点创建方法的对比

    本文主要介绍jQuery与JavaScript节点的创建方法,以及他们的具体代码实现方法,大家可以对比下他们之间的不同,希望对大家编写代码有所帮助
    2016-11-11
  • 纯JS代码实现一键分享功能

    纯JS代码实现一键分享功能

    在qq空间,新浪微博,人人网等网络平台大家经常可以看到一键分享功能,那么基于js代码是如何实现一键分享的呢?下面脚本之家小编给大家介绍js实现一键分享功能的代码,需要的朋友参考下吧
    2016-04-04
  • JavaScript编码风格指南(中文版)

    JavaScript编码风格指南(中文版)

    写作本文旨在加深自己印象,也为了更多人的了解到JS编码风格,提高自己的编码质量。想了解更多的内容请阅读《编写可维护的JavaScript》
    2016-08-08
  • 很多人都是用下面的js刷新站IP和PV

    很多人都是用下面的js刷新站IP和PV

    有人用JS做网站跳转,也有人用很多其他作弊的方法,今天又发现有太多的人用这样的方法实现两个站的IP共享,把http://www.jb51.net替换成自己的站
    2008-09-09
  • JS实现线性表的顺序表示方法示例【经典数据结构】

    JS实现线性表的顺序表示方法示例【经典数据结构】

    这篇文章主要介绍了JS实现线性表的顺序表示方法,简单分析了线性表的原理并结合实例形式给出了线性表的插入与删除实现技巧,需要的朋友可以参考下
    2017-04-04
  • 关于网页中的无缝滚动的js代码

    关于网页中的无缝滚动的js代码

    随便打开一个网页,基本上都会看到无缝滚动或者轮播图,比如淘宝还有360官网的首页。那么这些滚动效果是怎么实现的呢?下面小编给大家分享关于网页中的无缝滚动的js代码,感兴趣的朋友一起看下吧
    2016-06-06
  • Webpack中SplitChunksPlugin 配置参数详解

    Webpack中SplitChunksPlugin 配置参数详解

    这篇文章主要介绍了Webpack中SplitChunksPlugin 配置参数详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • JavaScript中深拷贝与浅拷贝详解

    JavaScript中深拷贝与浅拷贝详解

    大家好,本篇文章主要讲的是JavaScript中深拷贝与浅拷贝详解,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-02-02
  • javascript小数计算出现近似值的解决办法

    javascript小数计算出现近似值的解决办法

    在javascript里面,小数只能进行相似计算,例如:5.06+1.30,你得到的结果会是6.359999999999999,但有的小数计算又是正确的,如果计算出现了近似值,你可以用如下的方法计算。
    2010-02-02
  • JS拖拽插件实现步骤

    JS拖拽插件实现步骤

    实现JS拖拽插件主要从六个方面做介绍:一、js拖拽插件的原理,二、根据原理实现的最基本效果,三、代码抽象与优化,四、扩展:有效的拖拽元素,五、性能优化和总结,六、jquery插件化 ,需要的朋友可以参考下
    2015-08-08

最新评论