jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on

本文深入探讨了JavaScript事件处理中的事件委托原理及其在jQuery库中的实现优化,通过比较不同绑定事件的方法(bind、live、delegate、on),详细分析了事件委托的优势与性能提升,特别强调了在大规模动态页面中使用事件委托的重要性,以及如何通过jQuery的.on()方法实现事件绑定的统一与高效。文章还讨论了不同事件处理方法的适用场景与限制,旨在帮助开发者在实际项目中做出更合理的事件处理策略选择。

事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应

事件的基础就不重复讲解了,本来是定位源码分析实现的, 所以需要有一定的基础才行

为了下一步更好的理解内部的实现,所以首先得清楚的认识到事件接口的划分

网上资料遍地都是,但是作为一个jQuery系列的源码分析,我还是很有必要在重新总结一下

jQuery对事件的绑定分别有几个API http://www.css88.com/jqapi-1.9/

.bind()

.live()

.delegate()

.on()

不管是用什么方式绑定,归根到底还是用addEventListener/attachEvent处理的,正如选择器一样不管如何匹配最终还是那么几个浏览器接口处理

既然如此,事件为什么还要区分那么多不同的处理方案?

这里就要涉及到DOM事件处理模型了,就是常提到的捕获冒泡

 


传统的事件处理:

给某一元素绑定了一个点击事件,传入一个回调句柄处理

element.addEventListener('click',doSomething,false)

这样的代码再正常不过了

但是,如果页面上有几个百元素需要绑定(假设),那么务必就要绑定几百次啦.

这样问题就出现了:

第一:大量的事件绑定,性能消耗,而且还需要解绑(IE会泄漏)

第二:绑定的元素必须要存在

第三: 后期生成HTML会没有事件绑定,需要重新绑定

第四: 语法过于繁杂

…………

有没有办法优化呢? 答案是肯定的

 


事件委托

DOM有个事件流的特性,也就是说我们在页面上触发节点的时候事件都会上下或者向上传播,事件捕捉和事件冒泡。

借个图:

DOM2.0模型将事件处理流程分为三个阶段:一、事件捕获阶段,二、事件目标阶段,三、事件起泡阶段。

事件传送可以分为3个阶段。

(1).在事件捕捉(Capturing)阶段,事件将沿着DOM树向下转送,目标节点的每一个祖先节点,直至目标节点。例如,若用户单击了一个超链接,则该单击事件将从document节点转送到html元素,body元素以及包含该链接的p元素。在此过程中,浏览器都会检测针对该事件的捕捉事件监听器,并且运行这件事件监听器。

(2)在目标(target)阶段,浏览器在查找到已经指定给目标事件的事件监听器之后,就会运行 该事件监听器。目标节点就是触发事件的DOM节点。例如,如果用户单击一个超链接,那么该链接就是目标节点(此时的目标节点实际上是超链接内的文本节点)。

(3).在冒泡(Bubbling)阶段,事件将沿着DOM树向上转送,再次逐个访问目标元素的祖先节点到document节点。该过程中的每一步。浏览器都将检测那些不是捕捉事件监听器的事件监听器,并执行它们。

利用事件传播(这里是冒泡)这个机制,就可以实现事件委托。

具体来说,事件委托就是事件目标自身不处理事件,而是把处理任务委托给其父元素或者祖先元素,甚至根元素(document)

 


jQuery的事件优化

这么好的特性jQuery当然不会放过,所以就衍生出  .bind()、.live() .on()和.delegate()

jQuery的事件绑定有多个方法可以调用,以click事件来举例:

  • click方法
  • bind方法
  • delegate方法
  • on方法

这里要清楚的认识:不管你用的是(click / bind / delegate)之中那个方法,最终都是jQuery底层都是调用on方法来完成最终的事件绑定。

因此从某种角度来讲除了在书写的方便程度及习惯上挑选,不如直接都采用on方法来的痛快和直接

所以在新版的API中都这么写到:

.on()方法事件处理程序到当前选定的jQuery对象中的元素。在jQuery 1.7中,.on()方法 提供绑定事件处理的所有功能

 


性能对比

我们来个对直观的测试

生成999个DOM节点,不做任何处理,内存消耗2.2M

image

 


给每一个元素绑定click事件,增加到5.6M

$('.ul a').click(function(e){
        alert('click event');
    });

image

 


委托事件2.2M

$('.ul').on('click', 'a', function(e){
        alert('click event');
    });

image

 

效果不言而喻了,除了性能的差异,通过委托的事件还能很友好的支持动态绑定

只要on的delegate对象是HTML页面原有的元素,由于是事件的触发是通过Javascript的事件冒泡机制来监测,所以对于所有子元素(包括后期通过JS生成的元素)所有的事件监测均能有效,且由于不用对多个元素进行事件绑定,能够有效的节省内存的损耗。

 


.bind()

.bind()方法用于直接附加一个事件处理程序到元素上。

处理程序附加到jQuery对象中当前选中的元素,所以,在.bind()绑定事件的时候,这些元素必须已经存在

很明显就是直接调用的,没利用委托机制

 


.live()

将委托的事件处理程序附加到一个页面的document元素,从而简化了在页面上动态添加的内容上事件处理的使用。

例如:

$('a').live('click', function() { alert("!!!") });

JQuery把alert函数绑定到$(document)元素上,并使用’click’和’a’作为参数。

任何时候只要有事件冒泡到document节点上,它就查看该事件是否是一个click事件,以及该事件的目标元素与’a’这一CSS选择器是否匹配,如果都是的话,则执行函数。

 

因为更高版本的jQuery提供了更好的方法,没有.live()方法的缺点,所以.live()方法不再推荐使用

特别是,使用.live()出现的以下问题:

  • 在调用 .live() 方法之前,jQuery 会先获取与指定的选择器匹配的元素,这一点对于大型文档来说是很花费时间的。
  • 不支持链式写法。例如,$("a").find(".offsite, .external").live( ... ); 这样的写法是不合法的,并不能像期待的那样起作用。
  • 由于所有的 .live() 事件被添加到 document 元素上,所以在事件被处理之前,可能会通过最长最慢的那条路径之后才能被触发。
  • 在移动 iOS (iPhone, iPad 和 iPod Touch) 上,对于大多数元素而言,click 事件不会冒泡到文档 body 上,并且如果不满足如下情况之一,就不能和.live() 方法一起使用:
    1. 使用原生的可被点击的元素,例如, a 或 button,因为这两个元素可以冒泡到 document
    2. 在 document.body 内的元素使用 .on() 或 .delegate() 进行绑定,因为移动 iOS 只有在 body 内才能进行冒泡。
    3. 需要 click 冒泡到元素上才能应用的 CSS 样式 cursor:pointer (或者是父元素包含document.documentElement)。但是依需注意的是,这样会禁止元素上的复制/粘贴功能,并且当点击元素时,会导致该元素被高亮显示。
  • 在事件处理中调用 event.stopPropagation() 来阻止事件处理被添加到 document 之后的节点中,是效率很低的。因为事件已经被传播到 document上。
  • .live() 方法与其它事件方法的相互影响是会令人感到惊讶的。例如,$(document).unbind("click") 会移除所有通过 .live() 添加的 click 事件!

 


.delegate()

为了突破单一.bind()方法的局限性,实现事件委托,jQuery 1.3引入了.live()方法。后来,为解决“事件传播链”过长的问题,jQuery 1.4又支持为.live()方法指定上下文对象。而为了解决无谓生成元素集合的问题,jQuery 1.4.2干脆直接引入了一个新方法.delegate()。

使用.delegate(),前面的例子可以这样写:

$('#element).delegate('a', 'click', function() { alert("!!!") });

JQuery扫描文档查找(‘#element’),并使用click事件和’a’这一CSS选择器作为参数把alert函数绑定到(‘#element)上。

任何时候只要有事件冒泡到$(‘#element)上,它就查看该事件是否是click事件,以及该事件的目标元素是否与CCS选择器相匹配。如果两种检查的结果都为真的话,它就执行函数。

可以注意到,这一过程与.live()类似,但是其把处理程序绑定到具体的元素而非document这一根上

那么  (a).live()== (document).delegate('a') ?

可见,.delegate()方法是一个相对完美的解决方案。但在DOM结构简单的情况下,也可以使用.live()。

 


.on()

其实.bind(), .live(), .delegate()都是通过.on()来实现的,.unbind(), .die(), .undelegate(),也是一样的都是通过.off()来实现的

提供了一种统一绑定事件的方法

 


总结:

在下列情况下,应该使用.live()或.delegate(),而不能使用.bind():

  • 为DOM中的很多元素绑定相同事件;
  • 为DOM中尚不存在的元素绑定事件;

用.bind()的代价是非常大的,它会把相同的一个事件处理程序hook到所有匹配的DOM元素上
不要再用.live()了,它已经不再被推荐了,而且还有许多问题
.delegate()会提供很好的方法来提高效率,同时我们可以添加一事件处理方法到动态添加的元素上
我们可以用.on()来代替上述的3种方法

不足点也是有的:

  • 并非所有的事件都能冒泡,如load, change, submit, focus, blur
  • 加大管理复杂。
  • 不好模拟用户触发事件

如何取舍就看项目实际中运用了。


by Aaron: http://www.cnblogs.com/aaronjs/p/3440647.html
<think>好的,我现在需要解决用户在CentOS 7上编译FFmpeg时使用libfdk-aac静态库出现的undefined reference to `sqrt&#39;错误。这个问题看起来像是链接阶段缺少数学库导致的。首先,我需要回顾一下用户之前的讨论和提供的引用,看看有没有相关的线索。 根据用户提供的引用[1],用户在编译FFmpeg时使用--enable-libfdk-aac参数会报错,但换成--enable-libfdk_aacf反而正常。这可能是因为库名称或配置参数有误。不过当前的问题是关于sqrt未定义的引用,这可能与数学库的链接有关。 数学函数如sqrt通常在libm库中,编译器在链接时需要显式链接这个库。当静态链接时,可能需要手动指定-lm参数。在引用[2]中提到的pc文件里,Libs.private: -lm,说明libfdk-aac在私有依赖中引用了数学库。但在静态链接时,这些私有依赖可能需要显式传递给FFmpeg的编译配置。 用户之前的解决方案中,在编译FFmpeg时添加了--extra-libs="-lpthread -lm -ldl",这可能是为了处理静态链接时的依赖。现在出现sqrt未定义,可能是因为在配置FFmpeg时没有正确传递-lm参数,导致链接器找不到数学库。 另外,检查用户的引用[3]中的编译命令,他们复制了libfdk*到/lib64/,但没有提到显式链接数学库。这可能在某些情况下导致问题,尤其是在静态编译时。 解决方案可能是在FFmpeg的编译配置中添加-lm到链接器标志中,确保数学库被正确链接。例如,在./configure时,使用--extra-ldflags="-L/usr/local/fdk-aac/lib -static -lm"。或者,在--extra-libs中添加-lm。 需要验证用户之前的步骤是否正确添加了数学库的链接。如果用户按照之前的指导已经添加了这些参数,但问题仍然存在,可能需要检查libfdk-aac的静态库是否正确编译,是否依赖其他库。另外,可能需要检查编译器的版本和路径是否正确,确保没有其他库冲突。 最后,可能需要指导用户如何检查编译日志,确认-lm是否被正确传递给链接器,并确认libm.a是否存在于系统中。如果系统缺少静态数学库,可能需要安装glibc-static或其他相关软件包。</think>#### 一、问题根源定位 `undefined reference to &#39;sqrt&#39;`错误本质是**静态链接时未正确关联数学库(libm)**。数学函数`sqrt`的实现位于`libm.a`静态库中,需在编译时显式指定链接参数。以下是完整解决方案: --- #### 二、解决方案步骤 ##### 1. 验证数学库存在性 ```bash # 检查系统是否已安装静态数学库 ls /usr/lib64/libm.a || echo "未找到libm.a,需安装glibc-static" # 若缺失则安装 sudo yum install -y glibc-static ``` ##### 2. 修正libfdk_aac编译参数 重新编译`libfdk_aac`时添加数学库依赖: ```bash cd fdk-aac-2.0.2 # 清理旧编译文件 make distclean # 配置时显式添加数学库 ./configure \ --prefix=/usr/local/fdk-aac \ --enable-static --disable-shared \ LDFLAGS="-lm" # 强制链接数学库[^1] make -j$(nproc) && sudo make install ``` ##### 3. 调整FFmpeg编译配置 在原有编译参数基础上增加`-lm`链接指令: ```bash # 修改FFmpeg编译配置 ./configure \ --prefix=/usr/local/ffmpeg \ --pkg-config-flags="--static" \ --extra-cflags="-I/usr/local/fdk-aac/include" \ --extra-ldflags="-L/usr/local/fdk-aac/lib -Wl,-Bstatic -lfdk-aac -lm -Wl,-Bdynamic" \ # 静态链接数学库[^2] --enable-nonfree \ --enable-libfdk-aac \ --enable-gpl \ --extra-libs="-lpthread -lm -ldl" # 全局补充数学库 ``` --- #### 三、关键参数解析 | 参数/操作 | 作用 | |-----------|------| | `LDFLAGS="-lm"` | 强制`libfdk_aac`编译时链接数学库 | | `-Wl,-Bstatic -lfdk-aac -lm -Wl,-Bdynamic` | 精确控制静态/动态链接顺序[^3] | | `--extra-libs="-lm"` | 全局补充数学库依赖 | --- #### 四、深度验证方法 ```bash # 检查最终二进制文件的符号表 nm /usr/local/ffmpeg/bin/ffmpeg | grep &#39; sqrt&#39; # 预期输出应包含类似内容: U sqrt@@GLIBC_2.2.5 # 确认符号已正确解析 ``` --- #### 五、扩展注意事项 若仍出现类似错误(如`pow`或`log`未定义),需检查**所有依赖库的静态编译参数**: ```bash # 示例:x264静态编译需添加数学库 ./configure --enable-static --extra-ldflags="-lm" ``` --- [^1]: GNU ld手册关于`LDFLAGS`的说明 [^2]: FFmpeg官方文档关于静态链接的配置指导 [^3]: Linux动态链接器对`-Wl,-Bstatic`的参数解析机制 --- ### 相关问题 1. 如何解决静态编译FFmpeg时出现的`undefined reference to &#39;pthread_create&#39;`错误? 2. CentOS 7环境下如何批量验证所有静态依赖库的完整性? 3. 使用自定义编译链时如何避免动态库与静态库的路径冲突?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值