注意: Jack 已经 修复了这篇文章中提到的几乎所有问题 – 做得棒!
我们一直推迟讨论 jQuery 选择器在 1.1 新版本中的速度问题,直到我们的发布更接近完成——然而,似乎这个过程已经被 加快了。所以,既然已经公开了,让我们来看看 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 说:感谢你帮助我们保持警觉——我们期待着看到你的库改进,并让每个人都受益。