概述
本文详细分析jquery-1.11.1.js源码文件行数:4269~4906;
代码简介:定义了jQuery.event命名空间,存放了事件处理底层的工具函数add-增加事件监听,remove-移除事件监听,trigger-触发事件,dispatch-事件分发等,JQ对象利用JQ原型方法添加事件时,最终都会调用到jQuer.event里的工具方法。
下文进行详细代码分析。
jQuery.event
// 事件管理的工具函数
jQuery.event = {
global: {},
// 添加绑定事件
add: function( elem, types, handler, data, selector ) {
var tmp, events, t, handleObjIn,
special, eventHandle, handleObj,
handlers, type, namespaces, origType,
// 获取elem在jQuery.cache中对应的缓存(如果没定义_data会自动初始化一个新的)
elemData = jQuery._data( elem );
// elemData即缓存不存在,说明elem不适合绑定事件,直接返回
if ( !elemData ) {
return;
}
// 处理handler是一个object而不单纯是funciton的场景
if ( handler.handler ) {
handleObjIn = handler;
handler = handleObjIn.handler;
selector = handleObjIn.selector;
}
// 为handler增加一个独有id,用于remove判断解除绑定的是不是同一个回调(我估计是直接用===比较引用会损耗性能)
if ( !handler.guid ) {
handler.guid = jQuery.guid++;
}
// 初始化缓存里的events对象,后面用于保存事件类型和对应回调
if ( !(events = elemData.events) ) {
events = elemData.events = {};
}
// 初始化elemData.handle,统一执行所有回调的函数
if ( !(eventHandle = elemData.handle) ) {
eventHandle = elemData.handle = function( e ) {
// 分发处理事件,trigger方法中会使用原生监听方法将eventHandle作为原生方法的回调
return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ?
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
undefined;
};
// 防止IE内存泄露
eventHandle.elem = elem;
}
// 本方法支持事件类型用空格隔开传入,处理type保存了多种事件类型的场景,将其分割开
types = ( types || "" ).match( rnotwhite ) || [ "" ];
t = types.length;
while ( t-- ) {
// rtypenamespace这个正则是用来匹配"."前后片段的,最终过滤得到type是需要的事件类型
// 具体考虑场景暂时还不确定,估计是用来兼容一些传入了命名空间的场景
// 命名空间是提供一种根据不同命名空间来处理事件的机制
tmp = rtypenamespace.exec( types[t] ) || [];
// 由代码可推测tmp[1]是原始类型
type = origType = tmp[1];
// tmp[2]是命名空间,使用split分割成数组
namespaces = ( tmp[2] || "" ).split( "." ).sort();
//最终得到需要的type,不存在的话就循环下一个
if ( !type ) {
continue;
}
// 获取其special里保存的事件类型对应的对象
// 后面代码可知,load,focus,blur,click,beforeunload五种事件类型均有对应的特殊对象
// 不存在则使用空对象
special = jQuery.event.special[ type ] || {};
// 如果selector存在,则使用special.delegateType作为type,否则使用special.bindType
// special.delegateType和special.bindType都为空(前面special可能只是空对象),则不改变type
type = ( selector ? special.delegateType : special.bindType ) || type;
// 上一行代码type可能改变了,因此更新一下对应的special对象
special = jQuery.event.special[ type ] || {};
// 构造一个handleObj
// 这里使用extend方法将handleObjIn扩展进handleObj,假如handleObjIn为空,就会直接返回handleObj
// extend内部确定需要扩展的target时,使用的是形参长度,因此只要用来扩展的对象传了形参,即使为空,也不会变为将handleObj扩展进jQuery
// 只当传了一个参数时,target才会变为this,即jQuery本身
handleObj = jQuery.extend({
type: type,
origType: origType,
data: data,
handler: handler,
guid: handler.guid,
selector: selector,
needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
// 保存命名空间,网上查找用法说是可让用户根据命名空间,对事件进行不同的处理
namespace: namespaces.join(".")
}, handleObjIn );
// 初始化该事件类型的缓存
if ( !(handlers = events[ type ]) ) {
// 初始化缓存数组handlers,用于保存回调
handlers = events[ type ] = [];
handlers.delegateCount = 0;
// setup暂时不理解是什么东西
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
// 调用elem.addEventListener或elem.attachEvent监听事件,回调使用eventHandle
// eventHandle里面调用jQuery.event.dispatch,会对缓存里面的对应事件类型回调一一执行
if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false );
} else if ( elem.attachEvent ) {
elem.attachEvent( "on" + type, eventHandle );
}
}
}
if ( special.add ) {
special.add.call( elem, handleObj );
// 感觉这是多余的分支,从前面代码分析,handleObj.handler跟handler永远是相同的引用
if ( !handleObj.handler.guid ) {
handleObj.handler.guid = handler.guid;
}
}
// 将回调handleObj添加进handlers
if ( selector ) {
// 这里使用splice时插入对象(handlers.delegateCount是位置,第二个参数是0表明不删除,第三个是需要插入数组的对象,会被放在handlers.delegateCount前面)
// 由于是插入的,因此事件分发执行的时候也会被优先执行
handlers.splice( handlers.delega