比较 $("#foo .bar") 和 $(".bar", "#foo") 的性能
Posted
技术标签:
【中文标题】比较 $("#foo .bar") 和 $(".bar", "#foo") 的性能【英文标题】:Comparing the performance of $("#foo .bar") and $(".bar", "#foo") 【发布时间】:2012-01-25 19:01:19 【问题描述】:向下滚动查看getById.getByClassName
与qSA
的比较!
如果我们想选择 ID 为 "foo"
的元素内的所有 "bar"
类元素,我们可以这样写:
$( '#foo .bar' )
或者这个:
$( '.bar', '#foo' )
当然还有其他方法可以实现,但是为了这个问题,我们只比较这两种方法。
那么,以上哪种方法效果更好呢? (哪个需要更少的时间来执行?)
我已经编写了这个性能测试:
(function()
var i;
console.time('test1');
for( i = 0; i < 100; i++ )
$('#question-mini-list .tags');
console.timeEnd('test1');
console.time('test2');
for( i = 0; i < 100; i++ )
$('.tags', '#question-mini-list');
console.timeEnd('test2');
)();
您必须在 Stack Overflow 起始页上的控制台中执行它。我的结果是:
火狐: 测试1:~90ms 测试2:~18ms
铬: 测试1:~65ms 测试2:~30ms
歌剧: 测试1:~50ms 测试2:~100ms
所以在 Firefox 和 Chrome 中,第二种方法要快很多倍——正如我所预料的那样。然而,在 Opera 中,情况正好相反。我想知道这里发生了什么。
能否请您在您的机器上运行测试并解释为什么 Opera 的性能不同?
更新
我编写了这个测试,以调查 Opera 的 qSA 是否真的超级快。事实证明,确实如此。
(function()
var i, limit = 5000, test1 = 'test1', test2 = 'test2';
console.time( test1 );
for( i = 0; i < limit; i += 1 )
document.getElementById( 'question-mini-list' ).getElementsByClassName( 'tags' );
console.timeEnd( test1 );
console.time( test2 );
for( i = 0; i < limit; i += 1 )
document.querySelectorAll( '#question-mini-list .tags' );
console.timeEnd( test2 );
)();
同样,您必须在 Stack Overflow 起始页上的控制台中运行此代码。我使用了 IE9 的 Firebug Lite 小书签(因为该浏览器没有实现 console.time
)。
所以,我比较了这个方法:
document.getelementById( 'A' ).getElementsByClassName( 'B' );
到这个方法:
document.querySelectorAll( '#A .B' );
我已经在每个浏览器中连续执行了五次上述脚本。算术平均值为:
(所有数字都以毫秒为单位。)
因此,第一种方法的性能在测试的浏览器中几乎相同(16-36ms)。然而,虽然 qSA 与第一种方法相比要慢得多,但在 Opera 中它实际上更快!
所以,qSA优化是可能的,不知道其他浏览器在等什么……
【问题讨论】:
test1: 73ms
, test2: 11ms
。 Opera 是一个奇怪的浏览器,我不知道它为什么会滞后。
@Blender 请增加循环限制。我的笔记本电脑真的很慢,所以我选择了 100。尝试 1000。(小于4ms
的结果不可靠...)
您是否考虑过包含document.getElementById('foo').getElementsByClassName('bar')
以确保完整性?
不同的实现有不同的优化。要做什么? ;)
对于测试jsperf.com会是更好的选择。
【参考方案1】:
如果浏览器支持querySelectorAll
,并且如果您传递了一个有效的选择器(没有自定义的非 CSS 选择器),jQuery/Sizzle 将避免使用基于 javascript 的 Sizzle 引擎。
这意味着您最终是在比较 querySelectorAll
的实现,假设您正在测试支持它的浏览器。
jQuery 或 Sizzle 还使用了其他优化,因此在不同浏览器中比较不同类型的 DOM 选择时很棘手。
Opera 的性能结果似乎是因为他们有一个非常优化的querySelectorAll
实现。 qSA
是一种相对较新的方法,与 getElementsByTagName
等旧方法相比,在某些浏览器中的优化还不够。
【讨论】:
嗯,qSA 并不是那么新。它从永远在 Chrome 中,从 3.5 开始在 Firefox 中,从 8 开始在 IE 中。浏览器有足够的时间来优化它。为他们感到羞耻(看看我上面的新测试)。 @ŠimeVidas:是的,新的只是相对于其他一些方法。但你是对的。在我看来,他们有足够的时间进行优化。我相信他们最终会回来的。 @ŠimeVidas:我将提出另一种可能性。由于您的测试对结果没有任何作用,我想知道优化是否根本不打扰 DOM 选择。也许它调用了该方法,但放弃了搜索。这可以解释为什么第一个测试需要更长的时间(两个函数调用)。这当然是纯粹的猜测。 @ЖΞЖ 我可以确认 Opera 进行了查询。我已经“在实践中”测试了这两个查询 - 请参阅此处的代码:jsfiddle.net/hfNTK/1 如果您在 Opera 中执行最后一部分,您会看到标签确实有蓝色背景,并且执行速度非常快.. . @ŠimeVidas: 嗯...但是您的测试的计时部分不使用选择的结果,并且页面上没有任何元素。您是否粘贴了正确的链接?【参考方案2】:赢家是……
测试 3 $('#question-mini-list').find('.tags');
您建议的两种方法不等效。
测试 1:Sizzle 从右到左解析(不要要求它搜索页面上的任何元素,然后限制为一个 ID)。
测试2:使用字符串作为上下文一般是没有用的,使用元素作为上下文。
测试 3:查找具有 id 的元素非常快。一旦你到了那里,就可以轻而易举地专注于给定类的一个项目。
【讨论】:
"Sizzle 从右到左解析" - 我很想有一个来源。 Sizzle 有一个优化,如果字符串以 id 选择器开头,it uses that first,那么它们应该是等价的。 jQuery 在内部将$('.bar', '#foo')
转换为$('#foo').find('.bar')
。我认为它们是等价的。后者当然要快一些。
@ŠimeVidas: Click here 并查看倒数第二条评论。我还在视频中听到 Resig 谈到了这一点。我看看能不能再找到它。
@Sinetheta 问题不是最好的选择方式,而是为什么 Opera 与其他浏览器的行为不同。【参考方案3】:
作为参考,这要快 30 倍:
document.getElementById("foo").getElementsByClassName("bar");
参见 jsPerf:http://jsperf.com/jquery-selector-variations/3。这需要一个 shim 才能在旧版本的 IE 中工作。
虽然 jQuery 非常有用,但如果速度极快,它并不总是最适合这项工作的工具。
【讨论】:
那个“Plain JS”栏毁了图表:P
@ŠimeVidas - 是的,我也注意到了。我没想到会有如此戏剧性的差异。如果您只想要这些,您可以返回到以前的版本。有点显示有多少开销,解析通用选择器和一般的 jQuery 对象都可能存在。以上是关于比较 $("#foo .bar") 和 $(".bar", "#foo") 的性能的主要内容,如果未能解决你的问题,请参考以下文章