zoukankan      html  css  js  c++  java
  • 再次理解订阅发布模式

    之前的总结:设计模式之发布订阅模式

    为什么会有这种设计模式

    这里有个很好的回答:https://segmentfault.com/q/1010000002487388

    简单的基于对象的订阅发布模式

    function EventEmitter() {
        this._event = {}
    }
    
    EventEmitter.prototype.on= function (type, handle) {
        this._event[type] = this._event[type] || []
    
        this._event[type].push(handle)
    }
    
    EventEmitter.prototype.remove = function (type, handle) {
        var index = (this._event[type] || []).indexOf(handle)
    
        if(index !== -1) {
            this._event[type].splice(index, 1)
        }
    }
    
    EventEmitter.prototype.emit = function (type, data) {
        (this._event[type] || []).forEach(function (handle) {
            handle(data)
        })
    }
    
    var test = new EventEmitter();
    
    var handle1 = function (data) {
        console.log(data[0])
    }
    
    var handle2 = function () {
        console.log(data[1])
    }
    
    test.on('fetchData', handle1)
    
    test.on('fetchData', handle2)
    
    test.remove('fetchData', handle2)
    
    test.emit('fetchData', [1,2,3])
    
    // 1
    

    常见的使用场景:当我们在ajax的异步数据请求结束后,emit一个事件,外部可以通过监听这个事件执行不同的操作

    原生web api基于DOM元素的发布订阅

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        
        
        
        <title>Document</title>
    </head>
    
    <body>
        <div id="ele"></div>
        <script>
            // 基于对象的订阅/发布
    
            // 基于DOM元素的事件订阅/发布
            var event = new CustomEvent('add', {
                detail: 'hello'
            });
    
            var handle = function (e) {
                console.log('handle:' + e.detail);
            }
    
            var handle2 = function (e) {
                console.log('handle2:' + e.detail);
            }
    
            ele.addEventListener('add', handle)
    
            ele.addEventListener('add', handle2)
    
            ele.dispatchEvent(event); // handle:hello    handle2:hello
    
            ele.removeEventListener('add', handle2);
    
            ele.dispatchEvent(event); // handle:hello
        </script>
    </body>
    
    </html>
    

    使用ES6实现的发布订阅

    http://zchange.cn/posts/332959902.html

    class Event {
      constructor() {
        this._subscribers = new Map();
        this.__index = 0;
      }
    
      /**
       * 将订阅者信息存入list
       * @param {String} eventName 事件名称
       * @param {fn} callback 订阅回调
       * 通过Map来存取对应的订阅者
       * 监听同一个主体,下一次的不会覆盖上一次的监听
       * 返回订阅者名称,和对应的下标,可供后面销毁
       */
      subscribe(eventName, callback) {
        if (typeof eventName !== 'string' || typeof callback !== 'function') {
          throw new Error('parameter error')
        }
        if (!this._subscribers.has(eventName)) {
          this._subscribers.set(eventName,new Map());
        }
        // 订阅同一个主题通过_index不会覆盖上一次。
        this._subscribers.get(eventName).set(++this._index,callback);
        return [eventName, this._index]
      }
    
    
      on(eventName, callback) {
        return this.subscribe(eventName, callback);
      }
    
      /**
       * 发布信息
       * @param {String} eventName 订阅者名称
       * @param {any} args 参数
       */
      emit(eventName, ...args) {
        if(this._subscribers.has(eventName)){
          const eveMap = this._subscribers.get(eventName);
          eveMap.forEach((map) =>map(...args));
        }else{
          throw new Error(`The subscription parameter ${eventName} does not exist`)
        }
    
      }
    
      /**
       * 销毁对应订阅者
       * @param {String|Object} event 
       */
      destroy(event) {
        if (typeof event === 'string') {
          // 直接销毁对应订阅者
          if (this._subscribers.has(event)) {
            this._subscribers.delete(event)
          }
        } else if (typeof event === 'object') {
          // 通过订阅者名词和下标,销毁其中某一个订阅者
          const [eventName, key] = event;
          this._subscribers.get(eventName).delete(key);
        }
      }
    
      /**
       * 清除所有订阅者
       */
      remove() {
        this._subscribers.clear();
      }
    
    }
    
    const $event = new Event();
    const ev1 = $event.on('aa', (...agrs) => {
      console.log(...agrs);
      console.log(111);
    })
    const ev2 = $event.on('aa', (...agrs) => {
      console.log(...agrs);
      console.log(222);
    })
    setTimeout(() => {
      $event.emit('aa', '1', '2');
      $event.destroy();
      $event.remove();
    }, 500)
    

    原生web api实现依赖于dom元素,即发布者和订阅者都是dom元素,因此使用场景有限。基于对象的实现方式常用于跨组件之间的数据传递,可以更好的解耦发布者和订阅者之间的联系。

    常用网站: SegmentFault | GitHub | 掘金社区
  • 相关阅读:
    html5表单pattern属性配合正则验证电话和手机号码
    关于a href传参的中文乱码问题
    jq点击事件改变全局变量
    a标签带参页面跳转并在跳转页面接收参数
    json字段不存在,手动添加键值对
    各种类型相互转换
    Caused by: com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value '�й���׼ʱ��' is unrecognized or r
    微信公众号之微信登录失败,redirect_uri域名与后台配置不一致,错误代码10003
    前后端数据json交换的问题
    jQuery EasyUI 的editor组件使用
  • 原文地址:https://www.cnblogs.com/yesyes/p/15375988.html
Copyright © 2011-2022 走看看