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

posted @ 2018-12-01 18:13  仙人掌的成长  阅读(578)  评论(0编辑  收藏  举报