注意: Jack 已经 修复了这篇文章中提到的几乎所有问题——做得太好了!
我们一直推迟讨论新版 1.1 中 jQuery 选择器速度的问题,直到发布日期临近,但似乎这个过程已经被 提前了。既然已经尘埃落定,那就让我们来研究一下 jQuery 的选择器速度吧。
简而言之:在 jQuery 1.1 中,我们倾尽全力使 选择器速度飞快。事实上,根据我们进行的所有测试,我们的速度快于任何其他选择器库。在开发 1.1 版的过程中,Dean Edwards 的 cssQuery 远远超过了其他任何选择器库。它非常全面,而且速度极快。
今天,Jack Slocum 公布了他的新 DOMQuery 选择器库。简而言之:标准已经提高。他的库非常快,可能是目前最快的选择器库。
然而,在比较他的库和我们的库时,出现了一些我们想要澄清的错误。(Jack 和 jQuery 双方都犯了错误)(作为参考,这是我测试时使用的 比较套件。)
jQuery 完全支持所有属性选择器。
例如,[@foo]、[@foo=bar] 等等。值得注意的是,jQuery 在这种情况下使用的是 XPath 风格的语法。由于 Jack 的测试中没有考虑到这一点,因此看起来我们无法通过所有属性选择器测试。
我们的“elem:empty”运行良好。
您可以从 Jack 的测试中看到,所有选择器(除了 DOMQuery)都无法通过 :empty 测试——这主要是因为他将结果与 DOMQuery 进行比较,而 DOMQuery 的结果是错误的。规范规定,如果一个元素不包含任何子元素或文本节点,那么它就是空的。在这种情况下,似乎没有考虑到这一点。
[foo!=bar]、:first、:last 不属于任何规范。
……但它们存在于测试套件中。顺便说一下,jQuery 确实实现了 :first 和 :last,但没有实现 [foo!=bar](似乎只有 cssQuery 实现?)。总而言之,在您没有设计为做某件事的情况下,与其他人进行比较是非常奇怪的。
span:not(span.hl-code) 匹配什么?
这是一个奇怪的灰色地带,我还没有看到任何地方提到过,规范也无法澄清。最终的结果集应该是所有没有 hl-code 类别的 span 还是什么都没有,因为您已经过滤掉了所有 span?例如
// Finds nothing in both
span:not(span)
=> []
// Finds spans that don't have a class of 'foo', in both
span:not(.foo)
=> [ <span>, <span>, ... ]
// jQuery's interpretation of the combination:
$("span:not(span.foo)")
=> []
// DOMQuery's interpretation of the combination:
Ext.select("span:not(span.foo")
=> [ <span>, <span>, ... ]
我们完全承认在这方面可能犯了很大的错误——但我很好奇地想知道其他人怎么说,以及他们对规范的解释是什么。
DOMQuery 没有考虑到重复项。
目前,执行 Ext.select(“div div”) 返回的元素数量比只执行 Ext.select(“div”) 返回的元素数量还要多——执行 Ext.select(“div div div”) 返回的元素集又有所不同,但仍然比只执行 Ext.select(“div”) 返回的元素数量多。事实上,考虑到重复项是 JavaScript 选择器库中一个巨大的问题——而目前,只有 jQuery 做对了。
一个重要的问题是,考虑到重复项可能会非常昂贵(从计算的角度来看)——因此,DOMQuery 没有考虑到重复项,这就给人们一种速度提升的错觉。例如
// DOMQuery
Ext.select("div").elements.length
=> 246
Ext.select("div div").elements.length
=> 624
Ext.select("div div div").elements.length
=> 523
// jQuery
jQuery("div").length
=> 246
jQuery("div div").length
=> 243
jQuery("div div div").length
=> 239
DOMQuery 不支持多个过滤器:elem.foo[foo=bar] 或 elem.foo.bar
在实现这一点之前,与任何其他库进行比较都是不公平的。构建一个能够完全处理这些方面(例如:cssQuery、jQuery)的库需要付出巨大的代价。(无论是代码大小还是速度成本。)
DOMQuery 的 #id 选择器不检查上下文
您会注意到,如果您尝试执行类似以下的查询
Ext.select("div #badid").elements
=> [div#badid]
您会得到一个名为“badid”的元素——即使该元素实际上并不在 div 内部。由于 DOMQuery 代码中实际上没有进行有效性检查,因此它的速度非常快,但结果也非常错误。
我应该提到,在 1.1 版之前,jQuery 在这一点上也是错误的,因此这是一个很容易忽视的问题。
根元素去哪了?
您会发现,在 DOMQuery 中搜索“html”和“*”奇怪地缺少了一件显而易见的事情:HTML 元素。从所有查询中排除根 DOM 元素似乎有点奇怪;尤其是考虑到这一点是完全有效的:“html > body *”。
……为了公平起见——这里有一个关于我们自己的问题 :-)
我们的 :nth-child(even/odd) 有缺陷。
目前,它似乎只选择一个元素 (!?)。我已经为此提交了一份 工单,这个问题应该会在本周日发布的 1.1 版中得到解决。
总而言之,看到 DOMQuery 取得的速度飞跃真是太好了。选择器速度是竞争真正有意义的一个领域;每当出现新的速度提升时,每个人都会从中受益(用户、开发人员——所有人)。
事实上,查看了他的代码后,我已经有了一些关于如何提高 jQuery 速度的新想法!
所以,Jack:感谢您帮助我们保持警醒——我们期待着看到您的库不断改进,并让每个人都从中受益。