【问题标题】:How is the jQuery selector $('#foo a') evaluated?如何评估 jQuery 选择器 $('#foo a')?
【发布时间】:2012-11-20 15:36:28
【问题描述】:

作为 jQuery 代码 (https://coderwall.com/p/7uchvg) 的示例,我读到表达式 $('#foo a'); 的行为如下:

查找页面中的每个a,然后在#foo 中过滤a

而且看起来效率不高。

正确吗?如果是,我们应该如何以更好的方式做到这一点?

【问题讨论】:

  • @undefined 请看这个:coderwall.com/p/7uchvg
  • 谢天谢地,您链接到的帖子已在其 cmets 中得到更正。

标签: javascript jquery performance jquery-selectors sizzle


【解决方案1】:

这是正确的 - Sizzle(jQuery 的选择器引擎)的行为是 same way as CSS selectors。 CSS 和 Sizzle 选择器是 evaluated right-to-left,因此 #foo a 将找到所有 a 节点,然后通过从 #foo 下降的节点过滤这些节点。

您可以通过确保您的叶子选择器具有高特异性来改进这一点,通常是通过给它们一个类或 ID。

【讨论】:

  • 嘶嘶声可能,但是...在 Chrome 上,this jsperf 显示 $('#id span')$('span') 快 10 倍,我认为这与所描述的算法不一致。
  • 这可能是因为 jQuery 在 span 的情况下每次运行都必须装饰多个 DOM 节点(随着测试运行更多),而在 #id span 的情况下只有一个。查看这个 jsperf - jsperf.com/does-id-el-find-all-els-first/3 - 当您将 LTR 与 RTL 进行比较时,RTL 更快。
  • 另外值得注意的是,您可以使用find()children()等指定的JQuery函数绕过sizzle引擎。所以你可以使用$("#foo").find("a")而不是$("#foo a")
  • @Matanya - 事实上,Yury 的 jsperf 测试用例很好地证明了这一点。这样会快很多!
  • @YuryTarabanko:谢谢,这很有启发性。
【解决方案2】:

我们应该如何以更好的方式做到这一点?

使用 jQuery 的上下文参数。

$('a', '#foo');

现在 jQuery 将搜索 id: foo 元素上下文中的所有锚点。

在您的查询中,省略时默认为文档:

$('#foo a'); == $('#foo a', document); 

在这种情况下,您的查询确实效率不高。

你可以看看at this article

【讨论】:

【解决方案3】:

虽然 Sizzle 确实是一个从右到左的引擎(这与解释 css 的方式相同),但您的示例中的特定选择器不会选择页面上的所有锚元素然后过滤他们的父母匹配“foo”的id。 Sizzle 实际上优化了任何以 ID 开头的选择器,并将其用作整个选择的上下文,而不是使用文档。换句话说,您选择的选择器基本上翻译为:

document.getElementById("foo").getElementsByTagName("a")

真的,这根本不是一个糟糕的选择器。

然而,考虑到 jQuery 需要做的其他事情(包括循环元素以将它们合并到 jQuery 实例中),jQuery("#foo").find("a") 将始终是最快的,因为 jQuery为 id-only 选择器实现了一个 jQuery 对象创建快捷方式,然后它从 #foo 开始查找。

也就是说,Sizzle 本身在做Sizzle("#foo a")Sizzle("a", document.getElementById("foo")) 时并没有太大的不同,但是jQuery("#foo").find... 会因为jQuery 自己的ID 快捷方式而更快。

顺便说一下,我对 Sizzle 的评论是假设 querySelectorAll 没有被使用。如果是,Sizzle 只是将它传递给 qsa,这仍然不如使用 jQuery 的 ID 快捷方式快。

【讨论】:

    【解决方案4】:

    您可以使用 find() 对选择器顺序进行更精细的控制:

    $('#foo').find('a');
    

    使用更复杂的选择器当然会更令人印象深刻,您可以在其中链接 find() 和 filter()。

    备案$('#foo').find('a') === $('a','#foo')

    [更新]好的,我后来意识到这正是你的链接所说的......

    jQuery 选择器引擎 (Sizzle) 已于去年进行了重构,您将在此处找到详细说明: http://www.wordsbyf.at/2011/11/23/selectors-selectoring/

    【讨论】:

      【解决方案5】:

      不要在#foo 元素内使用a 进行过滤,只需将一个类附加到a 元素并获取类为$("a.class");a 元素。这样会更有效率。

      【讨论】:

        【解决方案6】:

        又一个“自己试试”:

        1. jsperf for various selectors on 10000 elements
        2. jsperf for various selectors on 300 elements
        3. jsperf for various selectors on a "more representative DOM"

        与“平面”DOM(1 和 2)似乎没有太大区别,但嵌套 DOM 的性能差异更大。

        另外请注意,一些测试用例没有选择正确的元素(即 $('.a')$('.a', context)),但我将它们从原始测试中删除只是为了比较。

        【讨论】:

          【解决方案7】:

          此示例将检索名为foo 的元素中的所有锚元素a,以查找页面中的每个a,然后根据需要过滤#foo 中的a 你应该选择a #foo

          $("a #foo");
          

          这将检索a 元素中的所有foo 元素。

          【讨论】:

          • "a #foo" 几乎不是最理想的。 id="foo" 只能有 1 个元素。
          • 我不明白为什么这个答案会受到如此多的反对。我认为H先生误解了这个问题,但这个问题措辞不佳。 “在#foo 中过滤a”几乎不是标准语法。
          猜你喜欢
          • 2019-12-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-02
          • 1970-01-01
          • 2012-06-12
          • 2012-06-16
          相关资源
          最近更新 更多