【问题标题】:jQuery optimization selectorjQuery 优化选择器
【发布时间】:2011-06-09 15:40:56
【问题描述】:

前几天我在想,当你在 jQuery 中输入选择器时,如果你非常具体或非常松散地指定你的选择器,会不会对性能产生影响。

例如

$('$myId .someElement')

或者

$('table#myId > tbody > tr > td > div.someElement');

如果您需要一遍又一遍地找到相同的元素,那么第二个选项会更快还是差异可以忽略不计,例如在执行.each() 时。

【问题讨论】:

    标签: jquery optimization


    【解决方案1】:

    更新 2:

    在这里总结一些其他的cmets。答案是视情况而定

    jQuery 的选择器引擎 Sizzle 评估选择器的方式与 CSS 相同:from right to left。所以一般来说,最好在右侧非常具体,在左侧不太具体。

    但这也取决于 HTML 的结构以及您要查找的元素的分散程度。

    如果你有

                             a
                             |
                            ...
                             |
                             b
                     -----------------
                     |               |
    d d d c c c c c   c c c c c c c c   c c c c c c
    

    然后更具体更快$('a b > c'),因为您正在减少使用b > c 的元素集,并且不必检查每个ca 继承。

    如果你有

                             a
                             |
                            ...
                             |
                             b
                     -----------------
                     |               |
    e e e e e e e e   d d c c c c c c   e e e e e e
    

    那么$('a c') 会更快,因为选择器更简单,并且在这种情况下测试c 是否是b 的子级是多余的(当然你甚至可以使用$('c'))。


    原答案

    不管哪一个更快,如果你必须一遍又一遍地访问一个元素,存储一个对它的引用

    var $element = $('#myId .someElement');
    
    $('some other selector').each(function() {
        // access $element here
    });
    

    The first selector seems to be a bit faster(在 Chrome 8 中)。

    在较新的浏览器(支持querySelectorAll)中,差异可能没有不支持它的浏览器那么大。

    更新:

    我认为这主要取决于 jQuery 可以使用多少内置方法。所以假设querySelector*不可用,第一个选择器可以翻译成

    document.getElementById('myId')[0].getElementsByClassName('someElement')
    

    对于第二个选择器,jQuery 必须额外检查一个元素是否确实是一个子元素。 IE。涉及更多处理。

    【讨论】:

    • 不要认为这是问题的答案。
    • @PeeHaa:嗯,我在这个问题中看到了两个部分:1)哪个选择器更快? 2) 在each 循环中使用它。我的回答是,哪个更快并不重要,因为您不应该一遍又一遍地搜索相同的元素:) 这可能不是问题的直接答案,但希望是解决实际问题的好方法。
    • +1,不是我一直在寻找的 awnser,但您确实指出了代码优化中的一个重要部分,存储在 vars 中比在循环内一遍又一遍地调用更好。 (顺便说一句,我不是新手;))
    • 使用a,b,c时,请尝试在图表中按顺序使用它们。使用它们的目的是让大脑不必费力去理解,当你安排它们的层次结构时,实际上会使解释复杂化。
    • @vol7ron: 好点...不知道为什么从一开始就没有这样做。
    【解决方案2】:

    我会选择:

    $('.someElement', '#myId')
    

    由于 getElementById 是最快的选择器操作,您首先过滤您的内容,然后搜索类。此外,选择器越少,选择就越快(取决于是否使用 descendantchild 选择器 - 检查 cmets!)。 Test 基于 @Felix 测试用例。

    编辑:所以要加强这个答案。基于jQuerydocumentation

    默认情况下,选择器执行它们的 从 DOM 开始搜索 文档根目录。然而,一个 可以为 使用可选的第二个搜索 $() 函数的参数。

    因此,当您提供第二个参数(上下文)时,jQuery 将首先搜索该 元素,然后在其中搜索,因此它将翻译自:

    $('.someElement', '#myId')
    

    收件人:

    $('#myId').find('.someElement')
    

    现在的诀窍是,尝试找到最近的元素容器,它实际上包含一个 ID 并将其放在 context 中,因为 ID 选择是 最快。现在你会说,为什么不使用已经翻译的第二个,因为我实际上并不在乎,因为第一个更干净而且速度也不慢:

                        $('.someElement', '#myId')                  $('#myId').find('.someElement')
    Chrome 8.0.552              36,527                                      37,400
    Firefox 3.6.13              17,632                                      19,155
    

    【讨论】:

    • 没想到这个结果,这绝对是正确答案!谢谢
    • @Michael:不客气,当您的问题处于活动状态时,请不要直接勾选回答按钮,尤其是当有像@Felix 这样的人参与时 ;-)
    • :D 我认为这是一种恭维 :) 顺便说一句。有趣的结果。我认为$('#myId .someElement')无论如何都会更快,因为它可以直接翻译成querySelectorAll('$myId .someElement'),但似乎$('#myId').find('.someElement')要快很多。哇。
    • 感谢 ifaour 的建议 :) 我确实是冲动地勾选了答案 ;)
    • @Felix Kling:正如我所说,我它的工作方式与 CSS 的工作方式相同..我只是不记得我在哪里读到了。
    【解决方案3】:
    '#myid .myclass'
    

    1) 查找所有具有 myclass 类的元素
    2) 对于每一个这样的元素,如果你到达#myid 祖先,就向上走它的祖先链并停止。如果没有这样的祖先,则丢弃该元素。

    现在,很可能是浏览器优化了这个过程,例如:

    1) 找到#myid 元素
    2) 查找所有具有myclass 类并且是#myid 元素的后代的元素


    table#myid > tbody > tr > td > div.mylcass
    

    请注意,在 ID 选择器前加上类型选择器没有任何效果,因为 ID 在文档中是唯一的。

    #myid > tbody > tr > td > div.mylcass
    

    使用类型选择器为类选择器添加前缀在性能方面更好。因此,如果您知道元素的类型,我们鼓励您设置它。

    我不确定那些子选择器。我想如果你使用它们应该会更快。但是快多少呢?可能忽略不计?此外,它看起来很丑。

    考虑一下:

    #myid td > div.myclass 
    

    如果#myid 是一个表格元素,并且你没有嵌套表格,那么这个选择器应该和你的第二个选择器一样快。我会用这个。

    【讨论】:

    • CSS 选择以这种方式工作(从右到左),但我认为 jQuery 不会。
    • @Šime - 是的有时。如果查询是 querySelectorAll 无法处理的内容,并且浏览器没有该功能,则不会。在这种情况下,是的,但总的来说,不会。
    • @nickf:刚刚发现了一些有趣的东西。事实上,Sizzle 似乎从右到左评估选择器:neeraj.name/2010/02/15/…
    • @Felix - 感谢您提供的信息 - 学习新东西总是很有趣。我想知道如果在表外的其他地方有(比如说)100 个带有class="someElement" 的元素,性能测试会如何进行。
    • @nickf:刚做了个测试,第二个确实快了。但是$(something, context) 仍然是最快的:jsperf.com/selector-test-100/5
    【解决方案4】:

    第二个会更快,因为它知道每一步要寻找什么。

    在这个例子中:

    $('table#myId > tbody > tr > td > div.someElement');
    

    如果没有 tbody,或者没有 tr,或者没有 td (等等),它可以直接快捷地退出搜索。此外,每个后续步骤都会缩小搜索范围(例如:不在<thead> 内搜索)。对比一下这个例子:

    $('#myId .someElement')
    

    这必须扫描#myId每个子元素。


    编辑:正如 FelixKing 指出的那样,您的第一个示例 $('#myId .someElement') 实际上更快。只有一点点,但仍然更快,所以我想我应该做出回应。与后代选择器(空格)相比,使用子选择器(>)是最快的遍历方法,因为可能的元素集要少得多。然而,现代浏览器具有高度优化的内置方法,jQuery 有时可以使用这些方法,实际上减少了执行时间。因此,正如在这种特殊情况下所见,“更差”的实际上更好,因为浏览器针对执行该特定查询进行了更好的优化。然而,总的来说,原始点仍然存在,特别是如果您正在使用浏览器无法优化的东西(例如自定义选择器)。

    【讨论】:

    • 您对此有任何基准测试结果,还是您只是假设?因为我的测试结果不同。
    • 我完全同意@Felix!我不认为第二个更快,并且确实测试结果表明它不是!...我认为(如果没有弄错的话)它与 CSS 选择(从右到左)的方法相同?
    • 当它出现逻辑时,我得出了同样的结论,尽管@ifaour 的测试结果指出了一些不同的东西
    • 我刚刚用一些嵌套的 div 进行了测试,第一个选择器仍然(如果只是稍微)更快:jsperf.com/selector-test-100/3
    • 我同意,如果你用纯 DOM 遍历方法来实现这两个选择器,那么第二个会更快。但是有优化的浏览器方法,必须考虑这些。
    猜你喜欢
    • 2011-08-27
    • 1970-01-01
    • 2011-11-18
    • 2011-05-05
    • 2020-06-21
    • 2011-05-23
    • 2014-11-25
    • 2011-03-12
    • 2014-08-12
    相关资源
    最近更新 更多