2

可能重复:
带有上下文的 jQuery 选择器的性能

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

我得到类似以下结果

没有缓存的个人:11490ms
有 $cache 的个人:13315ms
有 DOM 缓存的个人:14487ms

不带缓存的多个:7557ms
带 $cache 的多个:7824ms
带 DOM 缓存的多个:8589ms

有人可以对发生的事情有所了解吗?具体来说,当传入 jQuery 上下文时,为什么搜索会变慢?

编辑:

这里的大多数回答者(以及带有上下文的 jQuery 选择器的性能)基本上都说这个例子中的 DOM 太小而无法真正获得太多,或者ID无论如何选择都会很快。我理解这两点,我的问题的重点是为什么会context 减慢搜索速度,大小DOM不应该对此产生影响,而且通过 ID 搜索已经非常快的事实也不应该。

@pebble建议它较慢的原因是因为 jQuery 不能使用本机浏览器方法(getElementByID),这对我来说似乎很有意义,但是为什么在一个选择中搜索多个元素会更快呢?

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

4

4 回答 4

2

我想在很多情况下使用上下文会减慢速度,主要是因为 jQuery 会尽可能地尝试使用浏览器本地方法——而不是遍历整个 dom。这方面的一个例子是document.getElementById在你的例子中使用。

为什么减速?

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

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

您还会发现其他地方,根据您使用的选择器,您将获得性能提升 - 所有这些都取决于 jQuery 可以使用哪些方法来加速它的工作。例如:

$('.className');

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

$('.className .anotherClassName');

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

充分了解可用的本地方法将帮助您优化 jQuery 查询。


优化方法

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

$('div', context);

这将是因为getElementsByTagName已经存在自古以来一段时间,并且可以在纯 JavaScript 中直接在 DOM 元素上使用。但是,如果您要执行此操作,则执行以下操作可能会更快:

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

或者

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

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

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

$('h2 > a');

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

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

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

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


$().pushStack 和 $().add 的区别

针对 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()它来实现它的目标 - add 允许支持更多的数据类型 - 甚至是 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。

于 2012-10-24T21:14:44.140 回答
1

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

您可以使用http://jsperf.com/ 之类的东西使您的测试更容易

于 2012-10-24T21:14:35.153 回答
0

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

请记住,HTML 页面中的ID 应该是唯一的。因此,无论您在搜索 ID 时是否给它一个上下文,这都不会产生影响。

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

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

于 2012-10-24T21:18:40.873 回答
0

当您使用 id 时,您不会从上下文中受益,因为它在浏览器中已高度优化。

有了 id 你就可以打电话说嘿嘿。一个非编程示例,你在一个人的房间里,你喊出一个名字,那个人回答。

现在让我们看看上下文。假设你知道这个名字是一个男人的名字,所以你把房间分成男人和女人。你比问这群人的名字。一个相当简单的事情的额外步骤。

当您查找特定的事物(如属性)时,您将受益。浏览器更难查找且未高度优化的东西。假设您正在寻找具有特定属性的输入。最好引用一个你知道的包含它的元素,这样它就不必搜索页面上的每个输入。

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

所以而不是

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

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

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

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

但是现在有了 jQuery 对 querySelector 的处理,除非您使用的是 querySelector 不支持的组成的 jQuery 选择器,否则这些优化就更小了。对于不支持 querySelector 的浏览器,上下文很重要。

于 2012-10-24T21:37:23.220 回答