为什么$(div#id)比$(#id)慢?

4

除非我的测试有问题,否则在Chrome上运行此jsfiddle时,$("#id")选择器大约需要11ms,而$(div#id)选择器需要56ms。

$(document).ready(function(){
    startTime = new Date().getTime();
    for (i = 0; i < 10000; i++)
    {
        s = $("#idC12");
    }           
    $("#idResults").html("c12 by id only time: "+elapsedMilliseconds(startTime));

    startTime = new Date().getTime();
    for (i = 0; i < 10000; i++)
    {
        s = $("div#idC12");
    }           
    $("#classResults").html("c12 by tagname#id: "+elapsedMilliseconds(startTime));
});

function elapsedMilliseconds(startTime)
{
    var n = new Date();
    var s = n.getTime();
    var diff = s - startTime;
    return diff;
}

http://jsfiddle.net/MhWUc/


除了下面非常好的答案之外,我想说的是,由于页面上应该有唯一的ID,所以没有必要那么具体,如div#id... - Laurent S.
4个回答

11

那是因为$("#id")在内部使用本地的document.getElementById函数,该函数使用将id映射到具有此id的(唯一)元素的映射表。

这是jQuery源代码中相关的代码:

        // Easily-parseable/retrievable ID or TAG or CLASS selectors
        rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
        ...
        // Speed-up: Sizzle("#ID")
        if ( (m = match[1]) ) {
            if ( nodeType === 9 ) {
                elem = context.getElementById( m );
                // Check parentNode to catch when Blackberry 4.6 returns
                // nodes that are no longer in the document #6963
                if ( elem && elem.parentNode ) {
                    // Handle the case where IE, Opera, and Webkit return items
                    // by name instead of ID
                    if ( elem.id === m ) {
                        results.push( elem );
                        return results;
                    }
                } else {
                    return results;
                }
            } else {
                // Context is not a document
                if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
                    contains( context, elem ) && elem.id === m ) {
                    results.push( elem );
                    return results;
                }
            }

你会注意到:

  • 当正则表达式检测到 #someId 格式时使用它。
  • 任何提供的上下文只是添加一个测试,而不会使其更快。

请注意,即使在 jQuery 之外定义 CSS 规则或使用 document.querySelector 时,这条规则仍然适用:当你知道 ID 时,没有比使用 document.getElementById 更快的方式(除了缓存的元素...)。


准备好了,开始吧!你赢了 :) - jmar777
此外,$(div#id) 还必须在 div 上运行额外的选择器。我需要查看 Sizzle 引擎以查看是否首先查找并检查 $(#id) 是否为 div 类型,但我想它首先会运行 querySelectAll(div),然后再检查 id。 - Johnny Sparks
有时候我会忘记为什么要使用jQuery,但是当我看到其中的一些评论时,我就会想起来了。这是一个黑暗、黑暗的世界... - jmar777
1
同时,虽然id属性应该在HTML文档中是唯一的,但如果不是,则$("#id")仅会返回与该id匹配的第一个元素,就像getElementById()一样。但是,$("*#id")将返回所有满足条件的元素。 - J. Holmes

2

我已经有一段时间没有接触源代码了,但是我知道#some-id选择器过去是由document.getElementById()处理的,而更复杂的选择器(例如tagName#some-id)必须通过Sizzle最终通过document.querySelectorAll来处理。


1

$('div#id')较慢,因为它不能直接映射到本地的getElementById()方法。


0
当你使用div#id时,首先选择所有的div。
当你使用#id时,它直接跳转到id表格。

1
在底层实现上,它仍然是从右到左的实现,不是吗? - jmar777
@jmar777 我不这么认为。我认为这是CSS的工作方式,而不是jQuery的选择过程。我发誓我刚刚读到了关于这个的一些东西。也许我错了。我会去找一下。 - Ian
@jmar777 或者,这可能是一种特殊情况,即 jQuery 在字符串中查找并直接将调用映射到getElementById而不仅仅将其传递给querySelectorAll - Ian
从右到左?在获取容器中右侧的特定项目之前,不需要先获取左侧上的容器吗?也许只有在使用组合选择器时才需要使用空格/逗号运算符。 - Steve Wellens
1
@Ian 我认为该线程更适用于 #foo .bar 样式选择器,而不是 .foo#bar。提供的源代码中的正则表达式不匹配 tagName#id 选择器。然而,你可能对从右到左只应用于“组”级别是正确的。我想我需要更深入地挖掘这个问题... - jmar777
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接