jQuery Event 模块 源码浅解

本文深入探讨了jQuery如何通过内部机制高效地为DOM元素绑定及解除事件监听,包括使用单一处理函数来管理多种事件类型的方式,及其对内存使用和运行效率的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    众所周知,jQuery 通过jQuery.event.add & jQuery.event.remove 方法对DOM元素(文本和注释节点除外)进行事件的绑定和解绑。这两个方法都提供了四个参数,前三个参数elem, types, handler 是必选的,最后一个为可选参数分别是data和pos。

 

绑定:

    jQuery在对DOM元素进行绑定事件时,通过jQuery.data在jQuery.cache中存储绑定的事件类型、响应函数和可选参数data。该数据结构如下:

{
    events: {}
    handle: function(e){ }
}

events是一个对象,events中的属性值存储的是事件类型;值是一个数组。当为DOM元素绑定事件时,首先会创建一个包含响应函数、事件类型、guid和一些附加信息的对象,如果events中不存在值为该事件类型的属性,则添加一个值为该事件类型的属性,和一个值为空的数组,然后将刚创建的新对象push到数组中;如果存在,直接push到相对应的数组中。看下面这个例子就一目了然了:

//给id元素绑定两个单击和一个鼠标离开事件
$('#id').bind('click', function() { alert('once');  });
$('#id').bind('click', function() { alert('second'); });
$('#id').bind('mouseout', function(){ alert('mouseout'); });

//events对象的值
{
    'click': [
        {// 此对象还有:data,guid,namespace属性
            handler: function() { alert('once'); },
            type: 'click'
        },
        {
            handler: function() { alert('second'); },
            type: 'click'
        }
    ],
    'mouseout': [
        {
            handler: function(){ alert('mouseover'); },
            type: 'mouseout'
        }
    ]
}

handle是一个函数。jQuery为每一个DOM元素绑定的任意类型事件的响应函数都是此函数,并不是直接将用户传入的函数绑定为响应函数。该函数会在执行时调用一个公共方法jQuery.event.handle,通过apply更改this的指向,此函数还有一个静态属性elem,也是指向DOM元素自己,下面是jQuery中的源码:

//引用自jQuery源码
add: function(elem, types, handler, data) {
    var elemData = jQuery._data(elem).handle, // 从jQuery.cache中提取的数据
        eventHandle = elemData.handle;

    // 创建elem对象的事件响应函数
    if (!eventHandle) {
        elemData.handle = eventHandle = function(e) {
            return jQuery.event.handle.apply( eventHandle.elem, arguments ); // 改变对象的this指向
        };
    }

    // handle元素的静态属性elem
    eventHandle.elem = elem;

    while (type = types[i++]) {
        var handlers = events[ type ];

        // 查看type类型的事件是否存在
        if (!handlers) {
            handlers = events[ type ] = [];
            // 绑定事件
            elem.addEventListener(type, eventHandle, false);
        }

        handlers.push(handler);
    }
}

当用户触发事件后,jQuery.event.handle首先会调用jQuery.event.fix对Event对象做兼容处理,之后根据Event.type从jQuery.cache中获取用户绑定时传入的响应函数,逐个运行。

 

解绑

    主要思路是匹配guid,然后从jQuery.cache中events里删除包含相应的响应函数的对象。最后检查该DOM元素还有没有绑定的事件了,如果没有才是真的removeEventListener掉handle处理函数关删除jQuery.cache中的数据。

 

思考

    通过以上的了解,我们可以看到jQuery再为DOM元素的一种类型事件绑定多个响应函数时只会addEventListener一次,而不需要重复addEventListner,并且该DOM元素的所有类型的事件响应都指向一个函数。这时我的脑子里便浮出了这些问题(这里不谈浏览器兼容上比如IE的内存泄漏之类的问题):这样做的好处是啥?可以减少内存的使用?还是可以提高运行效率?或者说jQuery只是为了对内部事件模块的统一管理?这只是本人所想的到的一些问题,John Resig 这样设计肯定有他的考虑。

     接下来,本人就自己所想到的问题进行了思考。我们看看,这样做的唯一一点就是减少了JavaScript引擎对各个DOM元素本身相同类型事件的listening数,用一个对象将DOM元素绑定的响应函数重新组织了下,并增加了一些附加信息。难道说减少listening数可以减少对内存的使用,提高运行效率吗?如果是这样,我们便可以用最老土的方法来进行一些测试,以下是本人的测试方法和数据,仅供参考。

Chrome 11内存使用/MB解析时间/ms处理响应时间/ms
jQuery.bind17.544.7530.75
addEventListener21.75215.7511.5

 

以上数据是在window下的chrome 11.0.696.65环境中,绑定10 000个事件处理后,测试得到的平均值。当我绑定100 000个事件处理时,使用原生的addEventListener Chrome就提示无影响了,而用jQuery.bind是正常的。

    根据上面的数据,jQuery这样做却实能减少内存的使用和提高JavaScript引擎的解析时间,可是在触发事件后的响应处理要慢一点。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值