手写一个发布订阅模式
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);