0

0

js发布者订阅者模式使用详解

php中世界最好的语言

php中世界最好的语言

发布时间:2018-04-18 15:21:17

|

3199人浏览过

|

来源于php中文网

原创

这次给大家带来js发布者订阅者模式使用详解,js发布者订阅者模式使用的注意事项有哪些,下面就是实战案例,一起来看一下。

发布者订阅者模式,是一种很常见的模式,比如:

一、买卖房子

生活中的买房,卖房,中介就构成了一个发布订阅者模式,买房的人,一般需要的是房源,价格,使用面积等信息,他充当了订阅者的角色

中介拿到卖主的房源信息,根据手头上掌握的客户联系信息(买房的人的手机号),通知买房的人,他充当了发布者的角色

卖主想卖掉自己的房子,就需要告诉中介,把信息交给中介发布

二,网站订阅信息的用户

订阅者角色:需要订阅某类信息的网民,如某个网站的javascript类型文章

发布者角色:邮箱服务器,根据网站收集到的用户订阅邮箱,通知用户.

网站主想把信息告诉订阅者,需要把文章相关内容告诉邮箱服务器去发送

等等非常多的例子,不一一列举

本文用网站订阅的方式,推导发布者-订阅者框架,然后用发布者-订阅者框架来重构一个简单的购物车

var Site = {};
    Site.userList = [];
    Site.subscribe = function( fn ){
      this.userList.push( fn );
    }
    Site.publish = function(){
      for( var i = 0, len = this.userList.length; i < len; i++ ){
        this.userList[i].apply( this, arguments );
      } 
    }
    Site.subscribe( function( type ){
      console.log( "网站发布了" + type + "内容" );
    });
    Site.subscribe( function( type ){
      console.log( "网站发布了" + type + "内容" );
    });
    Site.publish( 'javascript' );
    Site.publish( 'html5' );

Site.userList就是用来保存订阅者

Site.subscribe就是具体的订阅者,把每一个订阅者订阅的具体信息保存在Site.userList

Site.publish就是发布者:根据保存的userList,一个个遍历(通知),执行里面的业务逻辑

但是这个,发布订阅者模式,有个问题,不能订阅想要的类型,上例我加了2个订阅者(第11行,第14行),只要网站发了信息,全部能收到,但是有些用户可能只想收到javascript或者html5的,所以,接下来,我们需要继续完善,希望能够接收到具体的信息,不是某人订阅的类型,就不接收

var Site = {};
    Site.userList = {};
    Site.subscribe = function (key, fn) {
      if (!this.userList[key]) {
        this.userList[key] = [];
      }
      this.userList[key].push(fn);
    }
    Site.publish = function () {
      var key = Array.prototype.shift.apply(arguments),
        fns = this.userList[key];
      if ( !fns || fns.length === 0) {
        console.log( '没有人订阅' + key + "这个分类的文章" );
        return false;
      }
      for (var i = 0, len = fns.length; i < len; i++) {
        fns[i].apply(this, arguments);
      }
    }
    Site.subscribe( "javascript", function( title ){
      console.log( title );
    });
    Site.subscribe( "es6", function( title ){
      console.log( title );
    });
    Site.publish( "javascript", "[js高手之路]寄生组合式继承的优势" );
    Site.publish( "es6", "[js高手之路]es6系列教程 - var, let, const详解" );
    Site.publish( "html5", "html5新的语义化标签" );

输出结果:

[js高手之路]寄生组合式继承的优势

[js高手之路]es6系列教程 - var, let, const详解

没有人订阅html5这个分类的文章

我们可以看到,只有订阅了javascript类型文章的人,才能收到 ”寄生组合式继承的优势” 这篇文章,发布html5类型的时候,没有任何人会收到.

es6类型的,只有订阅es6的人,才能收到

我们已经有了一个基本的发布订阅者框架,接下来,把他完善成一个框架,便于其他功能或者其他网站系统的相同功能可以重用他

var Event = {
      userList : {},
      subscribe : function (key, fn) {
        if (!this.userList[key]) {
          this.userList[key] = [];
        }
        this.userList[key].push(fn);
      },
      publish : function () {
        var key = Array.prototype.shift.apply(arguments),
          fns = this.userList[key];
        if (!fns || fns.length === 0) {
          console.log('没有人订阅' + key + "这个分类的文章");
          return false;
        }
        for (var i = 0, len = fns.length; i < len; i++) {
          fns[i].apply(this, arguments);
        }
      }
    };
    var extend = function( dstObj, srcObj ){
      for( var key in srcObj ){
        dstObj[key] = srcObj[key];
      }
    }
    var Site = {};
    extend( Site, Event );
     Site.subscribe( "javascript", function( title ){
      console.log( title );
    });
    Site.subscribe( "es6", function( title ){
      console.log( title );
    });
    Site.publish( "javascript", "寄生组合式继承的优势" );
    Site.publish( "es6", "es6系列教程 - var, let, const详解" );
    Site.publish( "html5", "html5新的语义化标签" );

然后,我们来重构一个购物车实例,没有重构之前,我的购物车用的是面向过程:




  
  Title
  


  • 0 单价: 15元; 小计: 0
  • 0 单价: 10元; 小计: 0
  • 0 单价: 5元; 小计: 0
  • 0 单价: 2元; 小计: 0
  • 0 单价: 1元; 小计: 0

商品一共 0 件; 一共花费 0 元; 其中最贵的商品单价是0

cart.js文件:

function getByClass(cName, obj) {
  var o = null;
  if (arguments.length == 2) {
    o = obj;
  } else {
    o = document;
  }
  var allNode = o.getElementsByTagName("*");
  var aNode = [];
  for( var i = 0 ; i < allNode.length; i++ ){
    if( allNode[i].className == cName ){
     aNode.push( allNode[i] );
    }
  }
  return aNode;
}
function getSubTotal( unitPrice, goodsNum ){
  return unitPrice * goodsNum;
}
function getSum(){ //计算总花费
  var aSubtotal = getByClass("subtotal");
  var res = 0;
  for( var i = 0; i < aSubtotal.length; i++ ){
    res += parseInt(aSubtotal[i].innerHTML);
  }
  return res;
}
function compareUnit() { //比单价,找出最高的单价
  var aNum = getByClass( "num");
  var aUnit = getByClass( "unit");
  var temp = 0;
  for( var i = 0; i < aNum.length; i++ ){
    if( parseInt(aNum[i].innerHTML) != 0 ){
      if( temp < parseInt(aUnit[i].innerHTML) ) {
        temp = parseInt(aUnit[i].innerHTML);
      }
    }
  }
  return temp;
}
window.onload = function () {
  var aInput = document.getElementsByTagName("input");
  var total = 0;
  var oGoodsNum = document.getElementById("goods-num");
  var oTotalPrice = document.getElementById("total-price");
  var oUnitPrice = document.getElementById("unit-price");
  for (var i = 0; i < aInput.length; i++) {
    if (i % 2 != 0) { //加号
      aInput[i].onclick = function () {
        //当前加号所在行的数量
        var aNum = getByClass( "num", this.parentNode );
        var n = parseInt( aNum[0].innerHTML );
        n++;
        aNum[0].innerHTML = n;
        //获取单价
        var aUnit = getByClass( "unit", this.parentNode );
        var unitPrice = parseInt(aUnit[0].innerHTML);
        var subtotal = getSubTotal( unitPrice, n );
        var aSubtotal = getByClass( "subtotal", this.parentNode );
        aSubtotal[0].innerHTML = subtotal;
        total++; //商品总数
        oGoodsNum.innerHTML = total;
        oTotalPrice.innerHTML = getSum();
        oUnitPrice.innerHTML = compareUnit();
      }
    }else {
      aInput[i].onclick = function(){
        var aNum = getByClass( "num", this.parentNode );
        if ( parseInt( aNum[0].innerHTML ) != 0 ){
          var n = parseInt( aNum[0].innerHTML );
          n--;
          aNum[0].innerHTML = n;
          //获取单价
          var aUnit = getByClass( "unit", this.parentNode );
          var unitPrice = parseInt(aUnit[0].innerHTML);
          var subtotal = getSubTotal( unitPrice, n );
          var aSubtotal = getByClass( "subtotal", this.parentNode );
          aSubtotal[0].innerHTML = subtotal;
          total--; //商品总数
          oGoodsNum.innerHTML = total;
          oTotalPrice.innerHTML = getSum();
          oUnitPrice.innerHTML = compareUnit();
        }
      }
    }
  }
}

耦合度太高,可维护性很差.

重构之后的购物车:

window.onload = function () {
  var Event = {
    userList: {},
    subscribe: function (key, fn) {
      if (!this.userList[key]) {
        this.userList[key] = [];
      }
      this.userList[key].push(fn);
    },
    publish: function () {
      var key = Array.prototype.shift.apply(arguments),
        fns = this.userList[key];
      if (!fns || fns.length === 0) {
        return false;
      }
      for (var i = 0, len = fns.length; i < len; i++) {
        fns[i].apply(this, arguments);
      }
    }
  };
  (function(){
    var aBtnMinus = document.querySelectorAll( "#box li>input:first-child"),
      aBtnPlus = document.querySelectorAll( "#box li>input:nth-of-type(2)"),
      curNum = 0, curUnitPrice = 0;
    for( var i = 0, len = aBtnMinus.length; i < len; i++ ){
      aBtnMinus[i].index = aBtnPlus[i].index = i;
      aBtnMinus[i].onclick = function(){
        (this.parentNode.children[1].innerHTML > 0) && Event.publish( "total-goods-num-minus" );
        --this.parentNode.children[1].innerHTML < 0 && (this.parentNode.children[1].innerHTML = 0);
        curUnitPrice = this.parentNode.children[4].innerHTML;
        Event.publish( "minus-num" + this.index, 
          parseInt( curUnitPrice ),
          parseInt( this.parentNode.children[1].innerHTML )
        );
      };
      aBtnPlus[i].onclick = function(){
        (this.parentNode.children[1].innerHTML >= 0) && Event.publish( "total-goods-num-plus" );
        this.parentNode.children[1].innerHTML++;
        curUnitPrice = this.parentNode.children[4].innerHTML;
        Event.publish( "plus-num" + this.index, 
          parseInt( curUnitPrice ),
          parseInt( this.parentNode.children[1].innerHTML )
        );
      }
    }
  })();
  (function(){
    var aSubtotal = document.querySelectorAll("#box .subtotal"),
      oGoodsNum = document.querySelector("#goods-num"),
      oTotalPrice = document.querySelector("#total-price");
      Event.subscribe( 'total-goods-num-plus', function(){
        ++oGoodsNum.innerHTML;
      });
      Event.subscribe( 'total-goods-num-minus', function(){
        --oGoodsNum.innerHTML;
      });
    for( let i = 0, len = aSubtotal.length; i < len; i++ ){
      Event.subscribe( 'minus-num' + i, function( unitPrice, num ){
        aSubtotal[i].innerHTML = unitPrice * num;
      });
      Event.subscribe( 'plus-num' + i, function( unitPrice, num ){
        aSubtotal[i].innerHTML = unitPrice * num;
      });
    }
  })();
  console.log( Event.userList );
}

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

es6解构有哪些方法

Node.js文件系统操作

我要服装批发网
我要服装批发网

由逍遥网店系统修改而成,修改内容如下:前台商品可以看大图功能后台商品在线添加编辑功能 (允许UBB)破解了访问统计系统增加整合了更加强大的第三方统计系统 (IT学习者v1.6)并且更新了10月份的IP数据库。修正了后台会员订单折扣金额处理错误BUG去掉了会员折扣价这个功能,使用市场价,批发价。这样符合实际的模式,批发价非会员不可看修正了在线编辑无法使用 “代码&rdqu

下载

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
node.js调试
node.js调试

node.js调试可以使用console.log()输出调试信息、断点调试和第三方调试工具。详细介绍:1、console.log()输出调试信息,通过在代码中插入console.log()语句,开发人员可以在控制台输出变量的值、函数的执行结果等信息,以便观察代码的执行流程和状态;2、断点调试,可以在代码中设置断点,以便在特定位置暂停代码的执行,观察变量的值和执行流程等。

348

2023.09.19

JavaScript 全栈开发基础(Node.js + 前端)
JavaScript 全栈开发基础(Node.js + 前端)

本专题系统介绍 JavaScript 在全栈开发中的核心知识结构,涵盖 Node.js 基础、Express/Koa 接口构建、前端交互设计、模块化与包管理、数据库连接、前后端数据通信与部署流程。通过完整项目示例,帮助学习者掌握从浏览器到服务器的一体化开发能力,实现真正意义上的全栈入门。

101

2025.11.26

html5动画制作有哪些制作方法
html5动画制作有哪些制作方法

html5动画制作方法有使用CSS3动画、使用JavaScript动画库、使用HTML5 Canvas等。想了解更多html5动画制作方法相关内容,可以阅读本专题下面的文章。

511

2023.10.23

HTML与HTML5的区别
HTML与HTML5的区别

HTML与HTML5的区别:1、html5支持矢量图形,html本身不支持;2、html5中可临时存储数据,html不行;3、html5新增了许多控件;4、html本身不支持音频和视频,html5支持;5、html无法处理不准确的语法,html5能够处理等等。想了解更多HTML与HTML5的相关内容,可以阅读本专题下面的文章。

435

2024.03.06

html5从入门到精通汇总
html5从入门到精通汇总

想系统掌握HTML5开发?本合集精选全网优质学习资源,涵盖免费教程、实战项目、视频课程与权威电子书,从基础语法到高级特性(Canvas、本地存储、响应式布局等)一应俱全,适合零基础小白到进阶开发者,助你高效入门并精通HTML5前端开发。

69

2025.12.30

html5新老标签汇总
html5新老标签汇总

HTML5在2026年持续优化网页语义化与交互体验,不仅引入了如<header>、<nav>、<article>、<section>、<aside>、<footer>等结构化标签,还新增了<video>、<audio>、<canvas>、<figure>、<time>、<mark>等增强多媒体与

104

2025.12.30

html5空格代码怎么写
html5空格代码怎么写

在HTML5中,空格不能直接通过键盘空格键实现,需使用特定代码。本合集详解常用空格写法:&nbsp;(不间断空格)、&ensp;(半个中文空格)、&emsp;(一个中文空格)及CSS的white-space属性等方法,帮助开发者精准控制页面排版,避免因空格失效导致布局错乱,适用于新手入门与实战参考。

78

2025.12.30

html5怎么做网站教程
html5怎么做网站教程

想从零开始学做网站?这份《HTML5怎么做网站教程》合集专为新手打造!涵盖HTML5基础语法、页面结构搭建、表单与多媒体嵌入、响应式布局及与CSS3/JavaScript协同开发等核心内容。无需编程基础,手把手教你用纯HTML5创建美观、兼容、移动端友好的现代网页。附实战案例+代码模板,快速上手,轻松迈出Web开发第一步!

158

2025.12.31

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

158

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 4.2万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.5万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号