在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个添加案例中,按类进行搜索,并且再次惊讶地发现这次搜索带缓存的多个类是最快的。
答案 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)');
在回应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选择元素的更快捷的方法是执行以下操作:
$(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的浏览器,上下文很重要。