手写Event.EventEmitter
一个**EventEmitter**需要实现以下方法:
- addEventListener
- removeEventlistener
- removeAllEventListener
- emit
- once
EventEmitter
function EventEmitter(){
this.events = new Map()
}
//工具函数
const wrapCallBack = (fn,once = false) => ({ callback:fn,once })
addEventListener
EventEmitter.prototype.addEventListener = function(type,fn,once = false){
//从events获取事件类型为type的事件回调
let handler = this.events.get(type);
// console.log(handler)
if(!handler){
//如果不存在 则添加
this.events.set(type,wrapCallBack(fn,once))
}else if(handler && typeof handler.callback === 'function'){
//如果只绑定一个回调
this.events.set(type,[handler,wrapCallBack(fn,once)])
}else {
//绑定类多个回调
this.events.set(type,[...handler,wrapCallBack(fn,once)])
}
}
removeEventListener
EventEmitter.prototype.removeListener = function(type,listener){
let handler = this.events.get(type);
if(!handler) return;
if(!Array.isArray(handler)){
//简单比较回调函数是否相同
if(String(handler.callback) === String(listener)){
this.events.delete(type)
}else {
return
}
}else{
//循环回调函数队列
for (let i = 0; i < handler.length; i++) {
const item = handler[i];
//简单比较回调函数是否相同
if(String(item.callback) === String(listener)){
handler.splice(i,1);
//避免回调函数数组变化 导致数组访问错误
i--;
}
}
//如果数组为空 删除监听的事件即可
if(handler.length === 0){
this.events.delete(type)
}else if(handler.length === 1){
//如果数组只有一个元素 只需要以对象的形式保存即可
this.events.set(type, handler[0]);
}
}
}
removeAllEventListener
EventEmitter.prototype.removeAllListener = function(type){
let handler = this.events.get(type);
if(!handler) return
this.events.delete(type)
}
emit
EventEmitter.prototype.emit = function(type,...args){
let handler = this.events.get(type),
eventsArray = [];
//如果是回调队列为数组的形式 循环遍历
if(Array.isArray(handler)){
//简单实现 避免后续数组长度出现变化 对数组的访问出错
//优化:可以考虑写一个工具函数 深拷贝
for (let i = 0; i < handler.length; i++) {
eventsArray.push(handler[i])
}
//遍历type对应的回调队列 触发每一个cb
for (let i = 0; i < eventsArray.length; i++) {
const item = eventsArray[i];
//执行回调
item.callback.apply(this,args);
if(item.once){
//如果回调函数只执行一次 则删除该回调函数
this.removeListener(type,item.callback)
}
}
}else {
//否则直接执行即可
handler.callback.apply(this,args)
}
return true
}
once
EventEmitter.prototype.once = function(type,fn){
this.addEventListener(type,fn,true)
}