程序员

手写一个发布订阅模式

2020-08-12  本文已影响0人  眸晓
// 1.先全局创建一个订阅对象
var eventObjs = {}

// 发布订阅事件
var events = {
  // 添加订阅
  on : function( type , callback ){
    eventObjs[ type ] = eventObjs[ type ] || [];
    const _event = eventObjs[ type ];
    let flag = true;
    for( let i = 0 ;i<_event.length;i++){
      if(_event [i] === callback){
        // 防止有重复的方法
        flag = false;
        return;
      }
    }
    flag && eventObjs[ type ].push(callback);   //eventObjs[ type ]不存在 就 默认设置为一个空数组
  },


  /**
   * 1.没有参数说明,取消所有订阅
   * 2.只有1个参数,说明取消订阅这个type下的所有方法
   * 3.两个参数,取消订阅type下的固定的一个方法
  */
  /**
   * 基本类型相互比较的是值,引用类型相互比较的是地址,基本类型和引用类型比较,
   * 是将引用类型转换为基本类型后在进行比较(==),但是===严格比较不会转换类型
  */
  off:function( type , callback ){
    let _event = eventObjs[type]
    // 取消订阅
    if(arguments.length === 0){
      eventObjs = {}
    }else if(arguments.length === 1){
      if(!_event) return
      _event = []
    }else if(arguments.length === 2){
      if(!_event) return
      /* 由于可能某type下有多个订阅,会存在删除行为,但遍历过程中,
      从下标为1处 边遍历边删会导致遍历的下标出现问题,所以使用倒序 */
      for(let i = _event.length; i >= 0 ; i--){
        if(_event[i] === callback){
          // callback是引用类型,比较的是引用地址
          _event.splice(i,1)
        }
      }
    }else{
      throw new Error('参数不正确')
    }
  },


  // 发布,订阅过的方法 —— 除了type可能还会有其他参数
  emit:function(type){
    let _event = eventObjs[type]
    if(!_event) return
    // 类数组无法使用Array原型链上的方法,但可以用角标,for循环,length属性
    let restArgs = Array.prototype.slice.call(arguments,1);    //由于arguments是类数组,不能直接执行slice,所以使用Array的原型调用方式,ES6中也可以使用Array.from转为数组后再操作
    for(let i = 0;i < _event.length; i++){
      _event[i].apply(null,restArgs)
      // _event[i].call(null,...restArgs)
    }
  }
}


var f1 = function(params){
  console.log('这个是f1',params);
}

var f2 = function(){
  console.log('这个是f2');
}

//订阅
events.on('click',f1)   
events.on('dbclick',f2)

// 发布
events.emit('click',1);

// 取消订阅
// events.off('click',f1);  
上一篇下一篇

猜你喜欢

热点阅读