发布-订阅模式

178 阅读1分钟

实现思路

  • 创建一个对象;
  • 在该对象上创建一个调度中心,实际上是一个缓存列表;
  • on 方法用来把函数 fn 都加到缓存列表中,也即订阅者注册事件到调度中心;
  • emit 方法取到 arguments 里第一个当做 event,根据 event 值去执行对应缓存列表中的函数(发布者发布事件到调度中心,调度中心处理代码);
  • off 方法可以根据 event 的值取消订阅;
  • once 方法只监听一次,调用完毕后删除缓存函数(订阅一次)

JavaScript代码实现

let eventEmitter = {
	//缓存列表
	list:{}, 
	// 订阅
	on(event, fn){
		let _this = this
		// 如果对象中没有对应的event的值,也就说明没有订阅过,就给event创建个缓存列表
		// 如果对象中有对应的event的值,把fn添加到对应的event的缓存列表里面
		(_this.list[event] || this.list[event] = []).push(fn)
	},
	off(event, fn){
		let _this = this;
		let fns = _this.list[event];

		//如果缓存列表中没有相应的 fn,则返回fasle
		if(!fns) return false;
		if(!fn){
			//如果没有传 fn 的话,就会将event值对应缓存列表中的 fn 都清空
			fns && (fns.length = 0)
		}else{
			for (let i = 0, cbLen = fns.length; i < cbLen; i++){
				cb = fns[i];
				if(cb === fn || cb.fn === fn){
					fns.splice(i,1);
					break;
				}
			}
		}
		return _this
	},
	emit(){
		let _this = this;
		// 第一个参数是对应的 evnet 值,直接用数组的shift
		let event = [].shift.call(arguments),
		fns =  _this.[event];
		// 如果缓存列表里面没有fn 就返回false
		if(!fns || fns.length === 0){
			return false
		}

		fns.forEach(fn => {
			fn.apply(_this, arguments)
		})
		return _this;

	},

	//监听一次
	once (event, fn){
		// 先绑定,调用后删除
		let _this = this;
		function on(){
			_this.off(event, on);
		}
		on.fn = fn;
		_this.on(event, on);
		return _this;
	}
}