使用JavaScript的事件委托是否比使用jQuery有性能优势?

3

JavaScript:

parent.addEventListener("click", function(e) {
  if (e.target === child) { code }
});

对比

jQuery:

$(parent).on("click", child, function() {});

这段代码是用 jQuery 写的,意思是给 parent 元素下的所有 child 元素添加点击事件。

2
纯JavaScript的方法可能会稍微更有效,因为jQuery以“易于编写”的方式包装代码,但性能差异可以忽略不计...而且纯JavaScript方法没有任何库依赖。 - Kinnectus
jQuery在内部执行的确切操作与纯JS中的操作相同,它基于选择器进行过滤,并检查事件目标和父级是否符合该选择器,这与您在纯JS中所做的操作相同。 - adeneo
3个回答

4
边缘情况下,jQuery的on方法会做更多的工作,为您提供额外的功能,例如在事件冒泡到根时运行选择器,因此事件来自您期望的元素;如果不小心,可能会有问题。引用文档,在事件性能下面:

在文档树顶部附近附加许多委托事件处理程序可能会降低性能。每次事件发生时,jQuery必须将该类型的所有附加事件的所有选择器与事件目标路径上的每个元素进行比较。

另一方面,纯JavaScript则更直接地引发在该元素下发生的任何事件,因此您必须非常具体。
话虽如此,在正常情况下,现代引擎下几百个事件处理程序的性能差异可以忽略不计。
然而,正如@Patrick所指出的那样,在某些特殊情况下,如果您需要跟踪和获取成千上万个元素的事件,则运行选择器将会严重影响性能。有时,您可以受益于使用特殊技术,例如仅将一个事件处理程序附加到这些元素的容器中,并使用e.target来查找实际引起事件的原因。

对于小容量事件处理(例如DOM事件),性能差异可以忽略不计,但对于像粒子系统中可能遇到的许多事件,存在显着差异。http://jsperf.com/jquery-events-vs-backbone-events-performance/3 - Patrick Gunderson
1
没错,我在最后加了一个提及这种情况的说明 :) - Jaime Gómez

1
问题表明,JS和jQuery中这些代码行看起来相同,但实际上并不同!

因此,我将我的答案分为两部分:

1.哪个JS / jQuery组合实际上是执行相同的操作,它们如何执行?

# suggested JavaScript version
parent.addEventListener("click", function(e) {
  if (e.target === child) { code }
});

# correct equivalent jQuery version
$(parent).on("click", function(e) {
  if (e.target === child) { code }
});

性能

简短版:性能差异微乎其微,不会有太大影响。

详细版:两个版本看起来几乎一样。jQuery 变体稍微慢一点,但可以忽略不计。如果我正在使用 jQuery(例如方法链接)或想要使用 jQuery 来提高可读性/一致性,我会使用 jQuery 版本。

出于速度原因而使用 JavaScript 版本,同时又为了在该项目中捍卫/推广/倡导使用 jQuery 而使用 jQuery 版本,这将产生利益冲突。

2. OP 的 jQuery 代码存在什么问题?

# OP’s version: jQuery selector/delegate syntax
$(parent).on("click", child, function() { code });
  • 实现并不是你想要或认为的那样
    • jQuery的选择器/委托语法并不是OP所想象的那样
    • 我也曾经掉进同样的陷阱,你呢?
  • 在做错误的事情时会烧掉额外的CPU周期
    • 这种性能惩罚远远超过了上述正确的jQuery版本的轻微惩罚
  • 文档不完善
    • jQuery文档对这个问题没有任何解释
    • 文档让人相信它会做我们大多数人认为它会做的事情
    • 让你相信“委托语法”是唯一(正确)的委托方式
  • 看起来非常简短和漂亮,但却很容易出错
    • 根据选择器的不同,在许多情况下可能会产生相同的结果
    • 一个正确的实现会进入jQuery吗?
      • 它会使用相同的简短语法吗?

本回答的其余部分通过示例展示了jQuery的错误:

我认为我们都同意一个事件应该最多调用一次处理程序。通过以下代码示例,我向您展示了一个单个事件如何触发3次处理程序的调用。

点击内部框查看jQuery如何将一个单一事件应用于目标元素和处理器元素之间的所有匹配元素。我不知道这种机制的合理用例,即使这种意外和奇怪的行为没有影响到您,jQuery所做的额外工作仍然存在。

它要么没效果只会浪费CPU周期,要么会创建错误。

let lastEvent;
function analyze(tag, event) {
  const oe = event.originalEvent;
  console.log(`${tag}: Event on ${event.target.id} is ${lastEvent === oe ? 'repeated' : 'new'}.`);
  lastEvent = oe;
}

body.addEventListener('click', event =>
  console.log('click', event.target.id));
$(body).on('click', analyze.bind(null, '$click'));
$(body).on('click', '.hit', analyze.bind(null, '$delegateClick'));
div.hit {
  margin: 20px;
  border: solid black 2px;
}

#inner {
  height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<body id=body>
  <div id=outer class=hit>
    <div id=middle class=hit>
      <div id=inner class=hit>
      </div>
    </div>
  </div>
</body>


我不确定我完全理解了,但我初步的想法是你有点刻意地编写了代码示例。对我来说,使用上面的“正确语法”大多意味着复制我实际上不必编写的代码,如果我使用选择器/委托语法。我知道你展示的代码指出了这个真正的潜在问题 - 然而,至少在我工作的项目规模上,我没有看到这成为一个明显的问题。我很乐意采用它提供的更简洁(并且随着时间更易读?)的语法。 - logicOnAbstractions
@logicOnAbstractions,适当的短期解决方案是编写一个小的jQuery扩展程序,提供正确实现“短语法”的方法。在我完成这个之前,我会坚守我的原则,并且无论代码大小如何,都会坚决避免有漏洞的代码。因为在我看来,那些做出与其所表现不同行为的代码是不“可读”的。 - Robert Siemer

-2
根据这个事件触发性能的jsperf,看起来jQuery比本地和Backbone事件慢得多。 Backbone事件比jQuery或本地事件快几个数量级。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接