【问题标题】:Is there a performance benefit to using JavaScript's event delegation rather than jQuery's?使用 JavaScript 的事件委托而不是 jQuery 有性能优势吗?
【发布时间】:2014-09-17 19:44:53
【问题描述】:

JavaScript

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

对比

jQuery

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

【问题讨论】:

  • 纯 Javascript 的效率可能稍微高一点,因为 jQuery 以“易于编写”的方式包装代码,但性能差异可以忽略不计……加上纯 Javascript 方法没有任何库依赖项。
  • jQuery 在内部执行完全相同的操作,它基于选择器进行过滤,并检查该选择器的事件目标和父级,这与在普通 JS 中所做的相同。

标签: javascript jquery performance


【解决方案1】:

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

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

另一方面,纯 javascript 做的事情更直接,只是引发在该元素下发生的任何事件,因此您必须非常具体。

话虽如此,现代引擎在正常情况下(例如几百个事件处理程序)的性能差异可以忽略不计。

然而,正如@Patrick 所指出的,对于需要跟踪和获取事件的数千个元素的某些特殊情况,这可能是灾难性的;在这种情况下,为每个事件运行选择器会破坏您的性能。有时您会受益于使用特殊技术,例如仅将一个事件处理程序附加到所述元素的容器并使用e.target 找出实际导致事件的原因。

【讨论】:

  • 对于小容量事件处理的性能差异可以忽略不计,就像您可能会在 DOM 事件中发现的那样,但对于您可能在粒子系统中发现的许多事件,却存在显着差异。 jsperf.com/jquery-events-vs-backbone-events-performance/3
  • 是的,我在最后添加了类似情况的提及:)
【解决方案2】:

问题表明 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 }
});

性能

短:性能差异很小,没关系。

Long:两个版本看起来几乎一样。 jQuery 变体有点慢,但可以忽略不计。如果我喜欢使用 jQuery(例如方法链接)或者想使用 jQuery 来提高可读性/一致性,我会使用 jQuery 版本。

出于速度原因使用 JavaScript 版本同时捍卫/促进/提倡在该项目中使用 jQuery 将是利益冲突。

2。 OP 的 jQuery 行有什么问题?

# OP’s version: jQuery selector/delegate syntax
$(parent).on("click", child, function() { code });

首先,jQuery 的选择器/委托语法并没有像 OP 认为的那样做。你很有可能落入同一个陷阱——就像我一样。

其次,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 {
  margin: 20px;
  border: solid black 2px;
}

#inner {
  height: 80px;
}
<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 正确的短期解决方案是编写一个小的 jQuery 扩展,提供“短语法”的正确实现。——在我这样做之前,我将保持我的原则并绝对避免错误代码代码大小无关紧要。因为在我的书中,做一些不同于看起来的事情的代码是不“可读的”。
【解决方案3】:

根据jsperf for event triggering performance,jquery 似乎比原生事件和 Backbone 事件慢得多。骨干事件比 jquery 或 navtive 事件快几个数量级。

【讨论】:

    猜你喜欢
    • 2014-11-16
    • 1970-01-01
    • 2011-02-28
    • 2013-06-04
    • 1970-01-01
    • 1970-01-01
    • 2015-01-06
    • 2015-09-04
    • 1970-01-01
    相关资源
    最近更新 更多