zoukankan      html  css  js  c++  java
  • js设计模式-发布/订阅模式

    一、前言

      发布订阅模式,基于一个主题/事件通道,希望接收通知的对象(称为subscriber)通过自定义事件订阅主题,被激活事件的对象(称为publisher)通过发布主题事件的方式被通知。

      就和用户订阅微信公众号道理一样,一个公众号可以被多个用户同时订阅,当公众号有新增内容时候,只要发布就好了,用户就能接收到最新的内容。

      js中的事件监听机制就是一种观察者模式。

    二、和观察者模式的区别

      观察者模式:一个对象(称为subject)维持一系列依赖于它的对象(称为observer),将有关状态的任何变更自动通知给它们(观察者)。

      1、Observer模式要求观察者必须订阅内容改变的事件,定义了一个一对多的依赖关系;
           2、Publish/Subscribe模式使用了一个主题/事件通道,这个通道介于订阅着与发布者之间;
           3、观察者模式里面观察者「被迫」执行内容改变事件(subject内容事件);发布/订阅模式中,订阅着可以自定义事件处理程序;
           4、观察者模式两个对象之间有很强的依赖关系;发布/订阅模式两个对象之间的耦合读底

    这是一个简单的实现,主要是创建一个对象,有三个属性(容器,订阅方法,发布方法)。将订阅者放入容器,发布,触发容器内的函数。

    (function(){
    
        //
        function Public(){
            //存放订阅者的容器
            this.subscribers=[];
            //添加订阅者
            this.addSubscribers=function(fn){
                let isExit = this.subscribers.some(function(sub){
                    return fn == sub;
                })
                if(!isExit){
                    this.subscribers.push(fn);
                }
                return this;
            }
        
        //发布消息
        this.deliver = function(data){
            this.subscribers.forEach(function(fn){
                fn(data);
            })
            return this;
        }    
        }
        
        let a = function(data){
            console.log("a:"+data);
        }
        let b = function(data){
            console.log("b:"+data);
        }
    
        let c = function(data){
            console.log("c:"+data);
        }
    
        var pub = new Public();
        pub.addSubscribers(a).addSubscribers(b).addSubscribers(c);
        pub.deliver("消息");
    })()

    2、可以看到观察者模式有如下优点

      a、每一个订阅者都是相互独立的只和发布者有关系,与发布者是一对多的关系,也可以是一对一的关系。
      b、每一个订阅者可以根据自己的需求来调用,而不影响其它订阅者
      c、与第一种方式相比,第二种方式的代码可读性、可维护性强;

    这是一个完整的实现

    (function(win){
        function Public(){
            this.handlers={};    
        }
        Public.prototype = {
            //订阅事件
            on:function(eventType,eventHandle){
                var self = this;
                if(!(eventType in self.handlers)){
                    self.handlers[eventType] = [];
                }
                self.handlers[eventType].push(eventHandle);
                return this;
            },
            emit:function(eventType){
                //如果调用函数传了多个参数,eventType指第一个参数,arguments是一个对象,参数序号是key指,同时也给他length
                //看起来像数组,其实不是数组。
                var self = this;
                //去除第一个事件类型的参数,使用call改变this指向
                //使用slice的对象需要由length属性,所以arguments才能使用成功。
                var handleArgs = Array.prototype.slice.call(arguments,1);
                console.log(handleArgs);
                for (var i =0; i<self.handlers[eventType].length;i++) {
                    //使用apply,订阅者的调用对象就是Public,不适用就是数组对象。
                    self.handlers[eventType][i].apply(self,handleArgs);
                }
                return this;
            },
            off:function(eventType,eventHandle){
                var currentEvent = this.handlers[eventType];
                var len = 0;
                if(currentEvent){
                    len = currentEvent.length;
    if(eventHandle == undefined){ currentEvent[eventType] = []; }else{ for (var i = len-1;i >= 0;i--) { if(currentEvent[i] == eventHandle){ currentEvent.splice(i,1); } } } } } } var a = function(data){ console.log(this); console.log("a"+data); } var b =function(data){ console.log("b"+data); }    var pub = new Public(); pub.on("click",a).on("click",b); pub.emit("click","xiaoxi"); })(window)

    参考博主的文章:https://www.cnblogs.com/leaf930814/p/9014200.html

    每一步都是一个深刻的脚印
  • 相关阅读:
    还在使用golang 的map 做Json编码么?
    Golang 性能测试(2) 性能分析
    golang 性能测试 (1) 基准性能测试
    消息队列 NSQ 源码学习笔记 (五)
    消息队列 NSQ 源码学习笔记 (四)
    消息队列 NSQ 源码学习笔记 (三)
    消息队列 NSQ 源码学习笔记 (二)
    消息队列 NSQ 源码学习笔记 (一)
    你不知道的空格
    Supervisor 使用和进阶4 (Event 的使用)
  • 原文地址:https://www.cnblogs.com/chzlh/p/10050487.html
Copyright © 2011-2022 走看看