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

一、前言

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

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

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

二、和观察者模式的区别

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

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

 三、实现一个简单demo

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

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 对象触发一个事件时,所有绑定在该事件上的函数都被同步地调用。 是不是很熟悉的感觉~~

1

2

3

4

5

6

7

8

9

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

上面的还是比较简答。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

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,在浏览器中实时变化的效果,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

//遍历传入实例的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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值