您的位置:首页 > Web前端 > JavaScript

javascript事件机制学习(一)——个人实现

2017-03-27 14:59 465 查看
 起因:在面试的时候被问到在不提供任何DOM API,不能使用jQuery,如何实现事件的监听和触发。

 需求:需要在全局范围监听事件。为了方便事件类型的扩展,提供注册事件,移除事件的方法。对于某一类的时间,提供监听事件,触发事件,和移除事件三种方法。因此针对需求,设计以下的数据结构:

 结构:用一个Object存储全部的事件,其中包括三个(或多个)handler,为addHandler,removeHandler和setupHandler。分别用于在第一次注册注册事件的时候,监听该类型的事件时,以及移除对改事件的监听时执行回调。EventQueue用于存储,监听该时间的全部事件对象。on用于将事件对象添加到响应的队列里面,off用于将事件对象从监听队列里移除,fire用于触发这一类事件。依次执行队列里面的时间。



代码分析:

整体数据结构:

var EventManager = function(){
this.eventMap = {};
this.handlers = ["add","remove","setup"];
this.guid = 0;
};

注册事件:

EventManager.prototype.registerEvent = function(name,eventObj) {
var eventMap = this.eventMap;

if(!eventMap.hasOwnProperty(name)){
eventMap[name] = {};

for(var i=0,len=this.handlers.length; i<len; i++){
var handlerName = this.handlers[i] + "Handler";
eventMap[name][handlerName] = eventObj[handlerName] || null;
}

eventMap[name]["queue"] = [];
}else{
for(var key in eventObj){
if( !key.test(/'queue'/g)){
eventMap[name][key] = eventObj[key];
}else{
eventMap[name][key].concat(eventObj[key].slice(0));
}

}
}

};


注册事件分为两种情况,一种是该事件已被注册的情况。这种情况,把提供的eventObj的时间队列添加到原有的队列后面。如果还没有注册该事件,则根据eventObj中提供的handler,创建新的监听对象。

移除事件类型:

EventManager.prototype.removeEvent = function(name){
var eventMap = this.eventMap;

if(!eventMap.hasOwnProperty(name)){console.log("has no such event type"); return ;}

delete eventMap[name];
}


先检查存不存在该类型的事件,存在的话,则从eventMap中delete掉。

监听事件:

EventManager.prototype.on = function(elem,type,callback){
var args = Array.prototype.slice.call(arguments, 0);
if(args.length < 3 || !this.eventMap.hasOwnProperty(type)) return ; // 如果参数提供的不齐全,或者没有该type,那么直接返回

//这里应该调整一下参数,先不实现
var queue = this.eventMap[type]["queue"],
addHandler = this.eventMap[type]["addHandler"],
setupHandler = this.eventMap[type]["setupHandler"],
i = 0, len = queue.length,dom = "dom";

for(; i<len; i++){
if(elem.nodeType == 1 && queue[i][dom] === elem){//该元素已经监听过的情况
addHandler &&addHandler.apply(elem);

queue[i]["callbacks"].push(callback);
break;
}
}

if(i == len){ // 该元素还没有监听任何事件
var temp = {
"id" : this.guid++,
"dom" : elem,
"callbacks" : []
};

setupHandler && setupHandler.apply(elem);
temp["callbacks"].push(callback);

queue.push(deepClone(temp));
temp = null;
}
}


首先需要确保监听类型,对象和回调都提供。并且要判断监听的事件类型是否存在。如果不满足条件,直接返回。

然后需要判断,该elem是否已经监听过这个事件了。如果有,则把callback放到原来的回调队列里面。如果还没有监听的话,那么产生一个{},其中包括id,执行的对象elem和回调队列。将该obj放到监听事件的队列里面。

触发事件:

EventManager.prototype.fire = function(elem,type){
var queue = this.eventMap[type]["queue"];

if(arguments.length < 2 || queue === undefined) return;

var i=0,len = queue.length;
for(; i<len; i++){
var context = queue[i]["dom"],
callbacks = queue[i]["callbacks"];

callbacks.forEach(function(callback){
callback.call(context,elem);
});
}
}
首先也要确保传参正确,并且存在该事件类型。如果没有,则直接返回。 然后依次取出queue里面的监听对象。获取执行回调的context和回调们。依次执行

移除事件:

EventManager.prototype.off = function(elem,type){
var queue = this.eventMap[type]["queue"];

if(arguments.length < 2 || queue == undefined) return ;

var i=0, len= queue.length,removeHandler = this.eventMap[type]["removeHandler"];

for(; i<len; i++){
if(elem.nodeType == 1 && queue[i]["dom"] === elem){
removeHandler && removeHandler.apply(elem);
queue.splice(i,1);
break;
}
}
}


找到该元素在该类型中的事件对象,然后从queue中移除。并且执行removeHandler

完整代码:
https://github.com/lizzyDeng/personalLib/blob/master/EventManager.js
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: