• 技术文章 >web前端 >js教程

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

    php中世界最好的语言php中世界最好的语言2018-04-18 15:21:17原创2032
    这次给大家带来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新的语义化标签" );

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

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <script src="js/cart.js"></script>
    </head>
    <body>
    <p id="box">
      <ul>
        <li>
          <input type="button" value="-">
          <span class="num">0</span>
          <input type="button" value="+">
          <span>单价:</span>
          <span class="unit">15元;</span>
          <span class="label">小计:</span>
          <span class="subtotal">0</span>元
        </li>
        <li>
          <input type="button" value="-">
          <span class="num">0</span>
          <input type="button" value="+">
          <span>单价:</span>
          <span class="unit">10元;</span>
          <span class="label">小计:</span>
          <span class="subtotal">0</span>元
        </li>
        <li>
          <input type="button" value="-">
          <span class="num">0</span>
          <input type="button" value="+">
          <span>单价:</span>
          <span class="unit">5元;</span>
          <span class="label">小计:</span>
          <span class="subtotal">0</span>元
        </li>
        <li>
          <input type="button" value="-">
          <span class="num">0</span>
          <input type="button" value="+">
          <span>单价:</span>
          <span class="unit">2元;</span>
          <span class="label">小计:</span>
          <span class="subtotal">0</span>元
        </li>
        <li>
          <input type="button" value="-">
          <span class="num">0</span>
          <input type="button" value="+">
          <span>单价:</span>
          <span class="unit">1元;</span>
          <span class="label">小计:</span>
          <span class="subtotal">0</span>元
        </li>
      </ul>
      <p class="total-box">
        商品一共
        <span id="goods-num">0</span>
        件;
        一共花费
        <span id="total-price">0</span>
        元;
        其中最贵的商品单价是<span id="unit-price">0</span>元
      </p>
    </p>
    </body>
    </html>

    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文件系统操作

    以上就是js发布者订阅者模式使用详解的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:javascript 发布者 详解
    上一篇:node.js操作音视频文件进行加密 下一篇:hammer.js实现移动端的图片手势放大功能
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【腾讯云】年中优惠,「专享618元」优惠券!• 一文搞定JavaScript的节点操作• JavaScript类数组和可迭代对象的实现原理详解• 带你深入了解HTTP模块• 聊聊Node.js path模块中的常用工具函数• 手把手带你使用node开发一个命令行压缩工具
    1/1

    PHP中文网