全程干货:带你实现mini版vue(一)

271 阅读4分钟

聊一聊发布-订阅模式

参考书籍:《JavaScript设计模式与开发实践》

发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状

态发生改变时,所有依赖于它的对象都将得到通知。在 JavaScript 开发中,我们一般用事件模型来替代传统的发布—订阅模式。

实例走起来:

假如小明和小红都想去学校小卖部买冰激凌,小明想吃绿豆味道,小红想吃红豆味道,此时这两种口味的没有了,老板说,现在没这两种口味了,等有了我电话通知你们再来买,ok,这就是一个简单的订阅模式

让我们用代码来实现以下吧!come on

// 核心超市        
let shop={            
  list:[], //定义订阅中心,用来存储订阅者           
  listen:function(fn){                
    this.list.push(fn)            
  }, //初始化订阅者            
  trigger:function(){                
    for (let index = 0; index < this.list.length; index++) {                    
      const element = this.list[index];                    
      element.apply(this,arguments)                
    }            
  }, //初始化发布者        
}         
shop.listen( function( price, taste){ // 小明订阅消息,要买绿豆味            
  console.log( '价格= ' + price );             
  console.log( '味道= ' + taste);         });        
shop.listen( function( price, taste){ // 小红订阅消息,要买红豆味          console.log( '价格= ' + price );             
  console.log( '味道= ' + taste);         });        
shop.trigger( 2, '绿豆' );        
shop.trigger( 3, '红豆' );

这里候我们发现了问题,小明并不想知道红豆的信息,反之亦然那么怎么办法?

// 核心超市        
let shop={            
  list:{}, //定义订阅中心,用来存储订阅者           
  listen:function(key,fn){  
    //如果订阅者的订阅空间不存在则创建
    if(!this.list[key]){      this.list[key]=[]    }              
    this.list.push(fn)            
  }, //初始化订阅者            
  trigger:function(){                
    // 取出消息队列的key,数组方法取出第一位参数    let key = Array.prototype.shift.call(arguments)     // 取出消息队列的函数    let fns = this.list[key]    // 如果不存在则没有订阅者进行订阅    if(!fns || fns.length===0){       return false    }                    for (let index = 0; index < this.list[key].length; index++) {         const element = this.list[key][index];         element.apply(this,arguments)     }           
  }, //初始化发布者        
}         
shop.listen('绿豆', function( price, taste){ // 小明订阅消息,要买绿豆味            
  console.log( '价格= ' + price );             
  console.log( '味道= ' + taste);         });        
shop.listen('红豆', function( price, taste){ // 小红订阅消息,要买红豆味          
  console.log( '价格= ' + price );             
  console.log( '味道= ' + taste);         });        
shop.trigger('绿豆', 2, '绿豆' );        
shop.trigger('红豆', 3, '红豆' );

此时就不会再订阅到别的信息了,到这里我相信你已经掌握了发布订阅的神髓,那么不妨让我们在发散一下,小明不想去小卖部买了,想去门口超市买,如何解决呢?难道我们要重写一遍代码吗?当然不,作为一门解释型语言,给对象动态添加属性是咱的本分啊!

// 核心超市        
let shop={            
  list:{}, //定义订阅中心,用来存储订阅者           
  listen:function(key,fn){  
    //如果订阅者的订阅空间不存在则创建
    if(!this.list[key]){      
        this.list[key]=[]    
    }              
    this.list.push(fn)            
  }, //初始化订阅者            
  trigger:function(){                
    // 取出消息队列的key,数组方法取出第一位参数    
let key = Array.prototype.shift.call(arguments)    
 // 取出消息队列的函数    
let fns = this.list[key]    
// 如果不存在则没有订阅者进行订阅    
if(!fns || fns.length===0){       
return false    
}                    
for (let index = 0; 
index < this.list[key].length; index++) {         
const element = this.list[key][index];         
element.apply(this,arguments)     
}           
  }, //初始化发布者        
}   
//如果我想订阅别的超市怎么办呢let initShop=function(obj){    for (const key in shop) {        obj[key]=shop[key]    }}   
// 这时候让我们来个大杯let shopPuls={}initShop(shopPuls)  
shopPuls.listen('绿豆', function( price, taste){ // 小明订阅消息,要买绿豆味            
  console.log( '价格= ' + price );             
  console.log( '味道= ' + taste);         });        
shopPuls.listen('红豆', function( price, taste){ // 小红订阅消息,要买红豆味          
  console.log( '价格= ' + price );             
  console.log( '味道= ' + taste);         });        
shopPuls.trigger( '绿豆',2, '绿豆' );        
shopPuls.trigger( '红豆',3, '红豆' );

这样,小明和小红就可以愉快的吃冰激凌了,过了几天由于冰激凌吃的太多,小明生病了,想取消订阅,这时候怎么办呢,让我们价格remove的方法吧!

// 核心超市        
let shop={            
  list:{}, //定义订阅中心,用来存储订阅者           
  listen:function(key,fn){  
    //如果订阅者的订阅空间不存在则创建
    if(!this.list[key]){      this.list[key]=[]    }              
    this.list.push(fn)            
  }, //初始化订阅者            
  trigger:function(){                
    // 取出消息队列的key,数组方法取出第一位参数    let key = Array.prototype.shift.call(arguments)     // 取出消息队列的函数    let fns = this.list[key]    // 如果不存在则没有订阅者进行订阅    if(!fns || fns.length===0){       return false    }                    for (let index = 0; index < this.list[key].length; index++) {         const element = this.list[key][index];         element.apply(this,arguments)     }           
  }, //初始化发布者 
  remove:function(key,fn){     let fns  = this.list[key]     // 没有订阅者直接返回     if(!fns){        return false     }     //有订阅者     if(!fn){         //没有回调函数,这标识取消所有的订阅         fns&&(fns.length=0)      }else{         for (let index = 0; index < fns.length; index++) {              const element = fns[index];              if(element===fn){                  fns.splice(index,1)              }          }      }   },//取消订阅       
}   
//如果我想订阅别的超市怎么办呢
let initShop=function(obj){    
for (const key in shop) {       
 obj[key]=shop[key]    
}
}   
// 这时候让我们来个大杯
let shopPuls={}
initShop(shopPuls)  
shopPuls.listen('绿豆', f1=function( price, taste){ // 小明订阅消息,要买绿豆味            
  console.log( '价格= ' + price );             
  console.log( '味道= ' + taste);         });        
shopPuls.listen('红豆', f2=function( price, taste){ // 小红订阅消息,要买红豆味          
  console.log( '价格= ' + price );             
  console.log( '味道= ' + taste);         }); 
shopPuls.remove('红豆',f2)       
shopPuls.trigger( '绿豆',2, '绿豆' );        
shopPuls.trigger( '红豆',3, '红豆' );

这样就可以取消订阅了!

新手处女作,感谢点赞收藏,大神轻点喷!