Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript实现发布订阅模式 #90

Open
GGXXMM opened this issue Jan 19, 2021 · 0 comments
Open

JavaScript实现发布订阅模式 #90

GGXXMM opened this issue Jan 19, 2021 · 0 comments
Labels
⭐️ js js knowledge

Comments

@GGXXMM
Copy link
Owner

GGXXMM commented Jan 19, 2021

一、发布订阅模式的概念

发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知。

二、JS实现发布订阅模式

用现实生活售楼买房的案例类比,来一步步实现发布订阅模式:

  1. 指定好发布的对象是谁?(比如售楼处)
  2. 给发布者一个缓存队列,存放回调函数以便通知订阅者(售楼处的花名册)
  3. 最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函
    数(遍历花名册,挨个发短信)。

简易版:

var salesOffices = {}; // 定义售楼处
salesOffices.clientList = []; // 缓存列表,存放订阅者的回调函数
// 添加订阅者
salesOffices.listen = function(fn){
 this.clientList.push(fn);
}
// 触发发布事件
salesOffices.trigger = function(){
  for(var i =0,fn;fn = this.clientList[i++];){
     fn.apply(this, arguments);
  }
}

下面进行简单的测试:

// 小明订阅消息
salesOffices.listen( function( price, squareMeter ){ 
  console.log( '价格= ' + price ); 
  console.log( 'squareMeter= ' + squareMeter ); 
});
// 小红订阅消息
salesOffices.listen(function (price, squareMeter) {
    console.log('价格 = ', price)
    console.log('squareMeter = ', squareMeter)
})
//触发
salesOffices.trigger(2000000, 90)
salesOffices.trigger(21321312321, 100)

上述代码,已实现了简单的发布订阅模式,但存在一些问题,订阅者接收到了发布者发布的每个消息,发布者给订阅者造成了些困扰。所以代码实现有必要增加一个key做标识,发布者只推送订阅者感兴趣的消息。假如小明又去另一售楼处,这段代码在另一个售楼处对象上又需要重写一次。

为了解决上述问题,将代码改进为通用版

通用版:

我们把发布订阅的功能提取出来,放在一个单独的对象:

var event = {
  clientList: [],
  listen: function(key, fn){
    if(!this.clientList[key]){
       this.clientList[key] = [];
    }
    this.clientList[key].push(fn);// 订阅的消息添加进缓存列表
  },
  trigger: function(key, ...args){
    let fns = this.clientList[key]
     if(!fns || !fns.length) {
          return false;
     }
     let tasks = fns.slice()
     for(let fn of tasks) {
        fn.apply(this, args)
     }
  },
  remove: function(key, fn){// 取消订阅
    var fns = this.clientList[key];
    if(!fns) return false;// 如果key对应的消息没有被订阅,则直接返回
    if(!fn){// 如果没有传入具体的回调函数,表示需要取消 key 对应消息的所有订阅
      fns && (fns.length = 0)
    }else{
      for(var l = fns.length-1;l>=0;l--){// 反向遍历订阅的回调函数列表
        var _fn = fns[l];
        if(_fn === fn){
          fns.splice(l,1);// 删除订阅者的回调函数
        }
      }
    }
  }
}

再定义一个installEvent函数,这个函数给所有对象都动态安装上发布-订阅的功能:

var installEvent = function(obj){
  for(var i in event) {
    obj[i] = event[i];
  }
}

测试下上述通用版的发布订阅的功能:

var salesOffices = {}; 
installEvent( salesOffices ); 
salesOffices.listen( 'squareMeter88', function( price ){ // 小明订阅消息
 console.log( '价格= ' + price ); 
}); 
salesOffices.listen( 'squareMeter100', function( price ){ // 小红订阅消息
 console.log( '价格= ' + price ); 
}); 
salesOffices.trigger( 'squareMeter88', 2000000 ); // 输出:2000000 
salesOffices.trigger( 'squareMeter100', 3000000 ); // 输出:3000000
@GGXXMM GGXXMM added babel ⭐️ js js knowledge and removed babel labels Dec 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
⭐️ js js knowledge
Projects
None yet
Development

No branches or pull requests

1 participant