jQuery上下文减慢了搜索速度

时间:2012-10-24 21:09:18

标签: javascript jquery performance jquery-selectors jquery-context

  

可能重复:
  Performance of jQuery selector with context

jQuery DOCS中说

  

默认情况下,选择器在DOM开始时执行搜索   在文档根目录。但是,可以给出替代上下文   使用可选的第二个参数到$()函数进行搜索。

基于此我的理解是,使用context作为第二个参数传入的选择应该比没有传递context的相同选择更快。但是我运行了一些测试,似乎好像不是这种情况,或者至少情况并非如此。

详细说明,我原本想看看是否一次搜索多个元素($("div1, #div2"))比单独搜索两个元素($("#div1") $("div2"))更快。然后,我决定使用context对其进行测试,但没有看到context的速度有多快,但当context似乎放慢速度时,我感到很惊讶

例如,给出以下基本HTML标记

<div id="testCnt">
    <div id="Div0"></div>
    <div id="Div1"></div>
    <div id="Div2"></div>
    <div id="Div3"></div>
    <div id="Div4"></div>
    <div id="Div5"></div>
    <div id="Div6"></div>
    <div id="Div7"></div>
    <div id="Div8"></div>
    <div id="Div9"></div>
</div>

以下JavaScript(jQuery 1.8.2,并使用FireBug测试)

$(function () {    
    var $dvCnt = $('#testCnt');
    var dvCnt = $dvCnt[0];

    console.time('Individual without cache');
    for (var i = 0; i < 10000; i++) {
        $('#Div0').text('Test');
        $('#Div1').text('Test');
        $('#Div2').text('Test');
        $('#Div3').text('Test');
        $('#Div4').text('Test');
        $('#Div5').text('Test');
        $('#Div6').text('Test');
        $('#Div7').text('Test');
        $('#Div8').text('Test');
        $('#Div9').text('Test');

    }
    console.timeEnd('Individual without cache');

    console.time('Individual with $cache');
    for (var i = 0; i < 10000; i++) {
        $('#Div0', $dvCnt).text('Test');
        $('#Div1', $dvCnt).text('Test');
        $('#Div2', $dvCnt).text('Test');
        $('#Div3', $dvCnt).text('Test');
        $('#Div4', $dvCnt).text('Test');
        $('#Div5', $dvCnt).text('Test');
        $('#Div6', $dvCnt).text('Test');
        $('#Div7', $dvCnt).text('Test');
        $('#Div8', $dvCnt).text('Test');
        $('#Div9', $dvCnt).text('Test');

    }
    console.timeEnd('Individual with $cache');

    console.time('Individual with DOM cache');
    for (var i = 0; i < 10000; i++) {
        $('#Div0', dvCnt).text('Test');
        $('#Div1', dvCnt).text('Test');
        $('#Div2', dvCnt).text('Test');
        $('#Div3', dvCnt).text('Test');
        $('#Div4', dvCnt).text('Test');
        $('#Div5', dvCnt).text('Test');
        $('#Div6', dvCnt).text('Test');
        $('#Div7', dvCnt).text('Test');
        $('#Div8', dvCnt).text('Test');
        $('#Div9', dvCnt).text('Test');

    }
    console.timeEnd('Individual with DOM cache');


    console.time('Multiple without cache');
    for (var i = 0; i < 10000; i++) {
        $('#Div0,#Div1 ,#Div2 ,#Div3 ,#Div4 ,#Div5 ,#Div6, #Div7, #Div8, #Div9').text('Test');
    }
    console.timeEnd('Multiple without cache');

    console.time('Multiple with $cache');
    for (var i = 0; i < 10000; i++) {
        $('#Div0,#Div1 ,#Div2 ,#Div3 ,#Div4 ,#Div5 ,#Div6, #Div7, #Div8, #Div9', $dvCnt).text('Test');
    }
    console.timeEnd('Multiple with $cache');

    console.time('Multiple with DOM cache');
    for (var i = 0; i < 10000; i++) {
        $('#Div0,#Div1 ,#Div2 ,#Div3 ,#Div4 ,#Div5 ,#Div6, #Div7, #Div8, #Div9', dvCnt).text('Test');
    }
    console.timeEnd('Multiple with DOM cache');
});

这是jsbin

我得到的结果如下:

没有缓存的个人:11490毫秒 带有$ cache的个人:13315ms
具有DOM缓存的个人:14487ms

多个没有缓存:7557ms
多个$ cache:7824ms
多个DOM缓存:8589ms

有人可以了解最新情况吗?特别是为什么在传递jQuery上下文时搜索速度变慢?

编辑:

这里的大多数anwsers(以及Performance of jQuery selector with context)基本上都说这个例子中的DOM太小而不能真正获得很多,或者ID选择是快速的,无论如何。我理解这两点,我的问题的主要问题是为什么context 缓慢向下搜索,DOM的大小不应该对此产生影响,也不应该通过ID搜索已经非常快。

@pebble建议其速度较慢的原因是因为jQuery无法使用本机浏览器方法(getElementByID),这似乎对我有意义,但为什么搜索速度更快一个选择中的多个元素?

无论如何,我将测试转移到jsPerf个添加案例中,按类进行搜索,并且再次惊讶地发现这次搜索带缓存的多个类是最快的。

4 个答案:

答案 0 :(得分:2)

我认为有很多情况下使用上下文会减慢速度,主要是因为jQuery会尽可能地尝试使用浏览器本机方法 - 而不是遍历整个dom。其中一个例子就是在您的示例中使用document.getElementById

为什么减速?

getElementById仅存在于文档对象上 - 您无法在上下文元素上使用它 - 即element.getElementById。所以我的理论是jQuery首先使用document.getElementById执行id请求,然后,如果有一个上下文集 - 扫描每个元素的父节点,告诉它们是否存在作为上下文的子节点 - 从而减慢过程。

可能很慢的选择器的其他示例

您还可以找到其他地方,根据您使用的选择器,您将获得性能提升 - 所有这些都归结为jQuery可以用来加速其工作的方法。例如:

$('.className');

最有可能转换为使用getElementsByClassName或提供的任何其他本机方法来按类名选择,但是:

$('.className .anotherClassName');

无法使用此(因为它必须考虑到这种关系)并且必须混合使用querySelector (如果存在) 和/或纯粹的javascript逻辑来解决问题。

充分了解可用的本机方法将有助于优化jQuery查询。


优化方法

如果您希望使用上下文进行优化,我认为这可以证明比没有上下文更快的查询:

$('div', context);

这是因为getElementsByTagName已经存在了一段时间,并且可以直接在DOM元素上用于纯JavaScript。但是,如果您要这样做,可能会更快地执行以下操作:

$().pushStack( context[0].getElementsByTagName('div') );

$( context[0].getElementsByTagName('div') );

主要是因为你减少了jQuery函数的调用,虽然这不那么简洁。关于许多流行的JavaScript环境需要注意的另一件事 - 调用没有参数的函数比调用要快得多

优化某些jQuery选择器的一种相对未使用的方法是使用jQuery eq伪选择器 - 这可以通过与SQL查询中使用LIMIT 0,1类似的方式加快速度 - 例如:

$('h2 > a');

将在所有H2中扫描寻找A元素,但是如果你从一开始就知道你的H2中只有一个A标记你可以这样做:

$('h2 > a:eq(0)');

另外,如果你知道只有一个H2 - 逻辑是相同的:

$('h2:eq(0) > a:eq(0)');


$()。pushStack和$()之间的区别。添加

在回应Jasper的评论时,这两个函数之间存在差异:

<强>。新增:

function (a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?
[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?
d:p.unique(d))}

<强> .pushStack:

function (a,b,c){var d=p.merge(this.constructor(),a);return
d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector
+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d}

主要区别在于.add()使用.pushStack()来实现它的目标 - 添加允许支持更多数据类型 - 甚至是jQuery对象。而.pushStack仅为DOM元素设计,如果您正在使用它,它会更加优化:)


按ID选择的更快捷方式?

这是显而易见的,但我想我会把它放在这里,因为有时会遗漏一些东西 - 通过id选择元素的更快捷的方法是执行以下操作:

$(document.getElementById('id'));

所有这些都是因为jQuery / Sizzle无法超越本机方法,这也意味着你可以避免在jQuery / Sizzle上进行任何字符串解析。尽管如此,它并不像jQuery对应的那样整洁,并且可能不会获得如此大的速度提升,但值得一提的是优化。如果您经常使用ID,则可以执行以下操作。

jQuery.byid = function(id){
  return jQuery(document.getElementById(id))
};

$.byid('elementid');

上面的内容会比我之前的例子略慢,但是仍然应该超过jQuery。

答案 1 :(得分:1)

由于您是按ID选择的,因此在这种情况下,jQuery(或嘶嘶声,我忘了)正在跳过更快的document.getElementById()。使用类时可能会得到不同的结果,但即使这样,它也可能因浏览器而异。

您可以使用http://jsperf.com/

之类的内容,让您的测试更轻松

答案 2 :(得分:0)

您似乎正在使用#elementid属性来执行测试。

请注意,HTML网页中的 ID 应该是唯一的。因此,如果您在搜索ID时给它一个上下文,这将没有什么区别。

如果您尝试使用类或元素标记本身来定位元素,则此测试可能更有意义。

              

$('.mydiv' , $('#innerDiv'))可能比$('.mydiv')

更快

答案 3 :(得分:0)

使用id时,您不会受益于上下文,因为它在浏览器中得到了高度优化。

有了id,你可以喊出来说嘿。一个非编程的例子,你在一个人的房间,你喊出一个名字,这个人回答。

现在让我们看一下上下文。让我们说你知道这个名字是一个男人的名字,所以你把房间分成了男人和女人。你要问这群男人的名字。一个额外的步骤,相当容易。

当您查找属性等特定内容时,您将受益匪浅。浏览器难以查找的东西并没有高度优化。假设您正在寻找具有特定属性的输入。最好引用一个你知道包含它的元素,这样就不必搜索页面上的每个输入。

现在有趣的部分是上下文选择器更慢。最好使用find。为什么?它必须处理多个jQuery对象的创建。 :)

所以而不是

    $('.myClass', dvCnt).text('Test');

DO

    $(dvCnt).find('.myClass').text('Test');

如果您要进行多次查找,最好将第一个存储到变量

    var myDiv = $(dvCnt)
    myDiv.find('.myClass1').text('Test');
    myDiv.find('.myClass2').text('Test');

但是现在jQuery正在对querySelector进行操作,除非你使用querySelector不支持的组合jQuery选择器,否则这些优化是一个较小的协议。对于不支持querySelector的浏览器,上下文很重要。