js设计模式之发布/订阅模式模式

一、前言

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

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

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

二、和观察者模式的区别

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

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

 三、实现一个简单demo

function Public(){
    //存放订阅者信息
    this.subscribers = [];

    //添加订阅者
    this.addSubscriber = function(subscriber){

      //保证一个订阅者只能订阅一次
      let isExist = this.subscribers.some(function(item){
        return item == subscriber;
      })

      if(!isExist){
        this.subscribers.push(subscriber);
      }
      
      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}`)
}

//初始化
let publisher = new Public();

//添加订阅者
publisher.addSubscriber(a);
publisher.addSubscriber(b).addSubscriber(c);

//公众号发布消息
publisher.deliver('这是公众号推送的第1条新信息!');  
publisher.deliver('这是公众号推送的第2条新信息!').deliver('这是公众号推送的第3条新信息!');  

看看node中的EventEmitter 对象触发一个事件时,所有绑定在该事件上的函数都被同步地调用。 是不是很熟悉的感觉~~

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
  console.log('触发了一个事件!');
});
myEmitter.emit('event');

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

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

四、完整的demo

上面的还是比较简答。

function Public() {
  this.handlers = {};
}
Public.prototype = {
    // 订阅事件
    on: function(eventType, handler){
        var self = this;
        if(!(eventType in self.handlers)) {
           self.handlers[eventType] = [];
        }
        self.handlers[eventType].push(handler);
        return this;
    },
     // 触发事件(发布事件)
    emit: function(eventType){
       var self = this;
       var handlerArgs = Array.prototype.slice.call(arguments,1);
       for(var i = 0; i < self.handlers[eventType].length; i++) {
         self.handlers[eventType][i].apply(self,handlerArgs);
       }
       return self;
    },
    // 删除订阅事件
    off: function(eventType, handler){
        var currentEvent = this.handlers[eventType];
        var len = 0;
        if (currentEvent) {
            len = currentEvent.length;
            for (var i = len - 1; i >= 0; i--){
                if (currentEvent[i] === handler){
                    currentEvent.splice(i, 1);
                }
            }
        }
        return this;
    }
};

var Publisher = new Public();

//订阅事件a
Publisher.on('a', function(data){
    console.log(1 + data);
});
Publisher.on('a', function(data){
    console.log(2 + data);
});

//触发事件a
Publisher.emit('a', '我是第1次调用的参数');

Publisher.emit('a', '我是第2次调用的参数');  

五、vue中的作用

  Vue会遍历实例的data属性,把每一个data都设置为访问器,然后在该属性的getter函数中将其设为watcher,在setter中向其他watcher发布改变的消息。

  这样,配合发布/订阅模式,改变其中的一个值,会发布消息,所有的watcher会更新自己,这些watcher也就是绑定在dom中的显示信息。

  从而达到改变浏dom,在浏览器中实时变化的效果,代码如下:

   //遍历传入实例的data对象的属性,将其设置为Vue对象的访问器属性
    function observe(obj,vm){
        Object.keys(obj).forEach(function(key){
            defineReactive(vm,key,obj[key]);
        });
    }
    //设置为访问器属性,并在其getter和setter函数中,使用订阅发布模式。互相监听。
    function defineReactive(obj,key,val){
        //这里用到了观察者(订阅/发布)模式,它定义了一种一对多的关系,让多个观察者监听一个主题对象,这个主题对象的状态发生改变时会通知所有观察者对象,观察者对象就可以更新自己的状态。
        //实例化一个主题对象,对象中有空的观察者列表
        var dep = new Dep();
        //将data的每一个属性都设置为Vue对象的访问器属性,属性名和data中相同
        //所以每次修改Vue.data的时候,都会调用下边的get和set方法。然后会监听v-model的input事件,当改变了input的值,就相应的改变Vue.data的数据,然后触发这里的set方法
        Object.defineProperty(obj,key,{
            get: function(){
                //Dep.target指针指向watcher,增加订阅者watcher到主体对象Dep
                if(Dep.target){
                    dep.addSub(Dep.target);
                }
                return val;
            },
            set: function(newVal){
                if(newVal === val){
                    return
                }
                val = newVal;
                //console.log(val);
                //给订阅者列表中的watchers发出通知
                dep.notify();
            }
        });
    }

    //主题对象Dep构造函数
    function Dep(){
        this.subs = [];
    }
    //Dep有两个方法,增加订阅者  和  发布消息
    Dep.prototype = {
        addSub: function(sub){
            this.subs.push(sub);
        },
        notify: function(){
            this.subs.forEach(function(sub){
                sub.update();
            });
        }
    }

  

 

转载于:https://www.cnblogs.com/leaf930814/p/9014200.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值