关于js中事件侦听和抛发机制的一些基础简单重构,通过这个简单重构了解中介者模式的工作方式,以下就是要做的简单重构方案
思路:
创建EmitterTarget类和EmitterEvent类。
EmitterTarget类主要使用了中介模式+观察者模式
其中EmitterTarget类的实例化属性list充当中介角色,每当执行add方法时,给EmitterTarget类的实例化对象注册事件名称及触发函数;每当该对象执行remove方法时,从list列表中移除注册的事件及触发函数;
dispatchEvent方法主要是发送事件对象本体,目的是为了在指定时刻触发事件函数以及传递数据参数。
==EmitterTarget类实例化对象本身就是观察者,观察者的特点是有add,remove,触发改变的函数(update或这里的dispatchEvent),以及应该有用于存放注册信息的属性或者单例对象。
==EmitterEvent类主要是用来创建事件对象本身,在创建时声明需要触发的事件类型以及需要携带的参数。
EmitterTarget.ts代码如下:
import EmitterEvent from "./EmitterEvent";
interface IEvent{//中介者即存储实例化对象绑定的事件的list的数据规范接口
[key:string]:Array<Function | null>;//key是事件名,value是触发事件函数数组
}
export default class EmitterTarget{
private list:IEvent={};//私有变量list,只用于方法内部数据存储通信
constructor(){
}
public addEventListener(type:string,handler:Function):void{
if(!this.list[type])this.list[type]=[];//根据实例化对象传进来的事件类型判断list列表中是否注册该事件类型,如果没注册过,则创建这个字段
var index=this.list[type].indexOf(handler);//看对应的事件中绑定的触发函数数组中是否存在这个触发函数
if(index>-1) this.list[type][index]=handler;//存在则覆盖
this.list[type].push(handler);//如果触发函数之前没有则在对应事件绑定的触发函数数组中新增
}
public removeEventListener(type:string,handler?:Function):void{
if(!this.list[type])return;//如果list注册表中无事件注册,则删除无意义,直接返回
if(handler===undefined){//如果调用该方法没有传要删除事件类型对应的触发函数就直接将该事件类型对应的全部触发函数删除
for(var i=0;i<this.list[type].length;i++){
this.list[type][i]=null;
}
this.list[type].length=0;
return;
}
var index=this.list[type].indexOf(handler as Function);//这里用了断言,传进来了事件对应的触发函数,所以这里直接删除指定的那个
if(index<0) return;
this.list[type][index]=null;
}
public dispatchEvent(evt:EmitterEvent):void{//抛发事件
if(!evt.type) throw new Error("错误的事件类型");//要抛发的事件一定要有事件类型
evt.currentTarget=this;//抛发先给参数,将抛发事件的主人绑定给这个抛发事件对象,以便后面注册列表,事件类型对应的触发函数中需要使用到
// var target=this;
// while(target){//这里相当于循环深度递归,去冒泡一直找当前this的父元素...(用于dom元素)
// if(target.parentElement)evt.path.push(target.parentElement);
// target=target.parentElement;
// }
if(!this.list[evt.type]) return;//再次重复一次,必须声明事件类型
for(var i:number=0;i<this.list[evt.type].length;i++){//根据事件类型去注册表里找对应的触发函数,这里执行的同时还要改变内部this执向
if(this.list[evt.type][i]) (this.list[evt.type][i] as Function).call(this,evt);
}
for (var j: number = 0; j < this.list[evt.type].length; j++){//这里再做了一次操作,在执行完注册表内事件类型对应的触发函数后,去将触发函数数组中为null的触发函数位置取消
// (可能程序的其他地方已经将数组里的某些触发函数删除了(执行removeEventListener方法), 但是没把占的位置取消, 这里将该位置取消了)
if(!this.list[evt.type][j]){
this.list[evt.type].splice(j,1);
j--;//splice()后j--是表示删除之后j需要回退一步,因为这里是遍历操作,
// 如果不回退则被删除元素后面那个元素就不会再被检测直接跳过
}
}
}
}
EmitterTarget.ts代码如下:
import EmitterTarget from "./EmitterTarget";
export default class EmitterEvent{
public type:string;
public currentTarget?:EmitterTarget;
//target.dispatchEvent(evt);如这里的evt.currentTarget===target(在dispatchEvent方法内部绑定evt.currentTarget=this)
public data?:object;//!!!注意这里的传的数据是用于注册的事件类型的触发函数中;
constructor(type:string,data?:object){//创建抛发的事件,主要有是事件类型,携带的数据,
this.type=type;
this.data=data;
}
}
使用demo:
1.定义需要使用事件抛发的类
import { IncomingMessage, ServerResponse } from "http";
import EmitterEvent from "./EmitterEvent";
import EmitterTarget from "./EmitterTarget";
import IRes from "./IRes";
import ResDataShow from "./ResDataShow";
interface IResData{
req:IncomingMessage;
res:ServerResponse;
data?:object;
}
export default class Main extends EmitterTarget{//main继承了EmitterTarget,所以这里有隐藏的list属性,用来充当注册表的角色
private static _instance?:Main;
constructor(){//给main的每个实例化对象都执行侦听ServerVo.DATA_LIST事件
super();
this.addEventListener(ServerVo.DATA_LIST,(e:EmitterEvent)=>this.dataHandler(e));//执行事件侦听方法时,在注册表中注册事件及其触发函数
}
static get instance():Main
{
if(!Main._instance) Main._instance=new Main();
return Main._instance;
}
private dataHandler(e:EmitterEvent){//事件侦听的触发函数即回调函数
var data:IResData=e.data as IResData;
var command:IRes;
command=new ResDataShow();
command.exec(data.req,data.res,data.data);
}
}
2.具体抛发操作
import EmitterEvent from "./EmitterEvent";
import Main from "./Main";
var evt=new EmitterEvent(type,{req:req,res:res,data:dataObj});
Main.instance.dispatchEvent(evt);//抛发事件,触发instance单例的绑定的函数