简体   繁体   English

Javascript getElementById查找 - 哈希映射还是递归树遍历?

[英]Javascript getElementById lookups - hash map or recursive tree traversal?

Does the DOM have a hash-table of elements whose keys are the elements' ids? DOM是否有一个元素的哈希表,其键是元素'ID?
I want to know if document.getElementById looks up a hash table or traverses the entire tree. 我想知道document.getElementById查找哈希表或遍历整个树。
Is this behavior different across browsers? 这种行为在浏览器中有所不同吗

I know about the Firefox and WebKit DOM implementations, both of them use a hashtable to lookup the elements, digging into the source of them you can give a look to the internal implementations: 我知道Firefox和WebKit DOM实现,它们都使用哈希表来查找元素,深入研究它们的来源,你可以看看内部实现:

WebKit implementation, Document.cpp , uses the hashtable if the id is unique, otherwise it traverses the document to get the first match: 如果id是唯一的,WebKit实现Document.cpp使用哈希表,否则它遍历文档以获得第一个匹配:

Element* Document::getElementById(const AtomicString& elementId) const
{
    if (elementId.isEmpty())
        return 0;

    m_elementsById.checkConsistency();

    Element* element = m_elementsById.get(elementId.impl());//<-- hastable lookup
    if (element)
        return element;

    if (m_duplicateIds.contains(elementId.impl())) {
        // We know there's at least one node with this id,
        // but we don't know what the first one is.
        for (Node *n = traverseNextNode(); n != 0; n = n->traverseNextNode()) {
            if (n->isElementNode()) {
                element = static_cast<Element*>(n);
                if (element->hasID() &&
                element->getAttribute(element->idAttributeName()) == elementId) {
                    m_duplicateIds.remove(elementId.impl());
                    m_elementsById.set(elementId.impl(), element);
                    return element;
                }
            }
        }
        ASSERT_NOT_REACHED();
    }
    return 0;
}

Firefox implementation, nsDocument.cpp Firefox实现, nsDocument.cpp

Implementations are free to do whatever they like, but since id is defined as a unique value, it would seem sensible to use a hash map or similiar lookup mechanism rather than traversal. 实现可以自由地做任何他们喜欢的事情,但由于id被定义为唯一值,因此使用哈希映射或类似的查找机制而不是遍历似乎是明智的。 What seems sensible from the outside, though, may not be when you get into the plumbing of building a complex web browser with many (sometimes conflicting) imperatives. 但是,当您进入构建具有许多(有时相互矛盾的)命令的复杂Web浏览器的管道时,外部看起来似乎是明智的。

I did an easy but very simplistic test (see page at end of answer). 我做了一个简单但非常简单的测试(请参阅答案末尾的页面)。 It's very simplistic not least because we don't know that browsers don't cache previous results. 非常简单,尤其是因为我们不知道浏览器不会缓存以前的结果。

Chrome 4.1.249.1059 reports: Chrome 4.1.249.1059报告:

ID: 0.0082ms per lookup
Tag: 0.0249ms per lookup

So, dramatically faster by ID than tag name. 因此,ID比标签名称快得多。

IE7 reports: IE7报告:

ID: 2.4438ms per lookup
Tag: 0.0437ms per lookup

So dramatically faster by tag name than ID (but remember IE7 has a broken concept of getElementById ; this is fixed in IE8). 标签名称比ID快得多(但请记住IE7有一个破坏的getElementById概念 ;这在IE8中得到修复)。

IE8 (on a different machine , don't compare absolutes, we're looking at diffs within the browser tested) reports: IE8 (在不同的机器上 ,不比较绝对值,我们正在查看测试的浏览器中的差异)报告:

ID: 1.1335ms per lookup
Tag: 0.0287ms per lookup

So about the same as IE7. 所以和IE7一样。

Firefox 3.6.3 reports: Firefox 3.6.3报告:

ID: 0.0042ms per lookup
Tag: 0.006ms per lookup

So it doesn't care that much (on repeated requests; again , it may be caching). 所以它并不在乎(重复请求; 再次 ,它可能是缓存)。

Opera 10.5.1 reports: Opera 10.5.1报道:

ID: 0.006ms per lookup
Tag: 1.467ms per lookup

Dramatically faster by ID than tag name. ID比标签名称快得多。

Make of those results what you will. 按照你的意愿制作这些结果。 A more complex test case would be needed to really infer the mechanisms. 需要一个更复杂的测试用例来真正推断出这些机制。 Of course, in at least two of those cases (Firefox and Chrome), we can go look at the source . 当然,至少在其中两个案例中(Firefox和Chrome), 我们可以看看源代码 CMS kindly points us to the WebKit and Firefox implementations (and looking at it, my suspicion about caching may have been on the money ). CMS友好地指出WebKitFirefox的实现(并且看着它,我对缓存的怀疑可能是 )。

Test page: 测试页面:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
    font-family: sans-serif;
}
#log p {
    margin:     0;
    padding:    0;
}
</style>
<script type='text/javascript'>
window.onload = pageInit;
function pageInit() {
    document.getElementById('btnGo').onclick = btnGoClick;
}
function btnGoClick() {

    log("Testing...");
    setTimeout(run, 0);
}

function run() {
    var count, time;

    try {
        // Warm up
        testid(10);
        testtag(10);

        // Test
        count = 10000
        time = testid(count);
        log("ID: " + (time / count) + "ms per lookup");
        time = testtag(count);
        log("Tag: " + (time / count) + "ms per lookup");
    }
    catch (e) {
        log("Error: " + (e.message ? e.message : String(e)));
    }
}

function testid(count) {
    var start;

    start = new Date().getTime();
    while (count-- > 0) {
        if (!document.getElementById('fred')) {
            throw "ID 'fred' not found";
        }
    }
    return new Date().getTime() - start;
}

function testtag(count) {
    var start;

    start = new Date().getTime();

    while (count-- > 0) {
        if (document.getElementsByTagName('em').length == 0) {
            throw "Tag 'em' not found";
        }
    }
    return new Date().getTime() - start;
}

function log(msg) {
    var log = document.getElementById('log');
    log.innerHTML += "<p>" + msg + "<\/p>";
}
</script>
</head>
<body><div>
<input type='button' id='btnGo' value='Go'>
<div id='log'></div>
<hr>
<div>test test<span>test<span>test<span>test<span>test</span></span></span></span></div>
<div>test test<span>test<span>test<span>test<span>test</span></span></span></span></div>
<div>test test<span>test<span>test<span>test<span>test</span></span></span></span></div>
<div>test test<span>test<span>test<span>test<span>test</span></span></span></span></div>
<!-- repeat the above a couple of thousand times; I had about 2,200 -->
<div>test test<span>test<span>test<span>test<span>test</span></span></span></span></div>
<div>test test<span>test<span>test<span>test<em id='fred'>test</em></span></span></span></div>
</div></body>
</html>

The specific implementation isn't defined in the HTML spec so it could easily vary browser to browser. HTML规范中未定义具体实现,因此可以轻松地将浏览器更改为浏览器。 For example IE documentation states 例如IE文档说明

Returns a reference to the first object with the specified value of the ID or NAME attribute. 返回对具有指定ID或NAME属性值的第一个对象的引用。

so I'd be tempted to say it does a search (or it just throws out elements in cases of hash collisions). 所以我很想说它会进行搜索(或者只是在哈希冲突的情况下抛出元素)。

EDIT Also keep in mind there are other data structures (like trees) that allow for access time somewhere between constant and linear. 编辑还要记住,还有其他数据结构(如树)允许访问时间介于常量和线性之间。

It shouldn't be hard to test. 它应该不难测试。

If it's tree-based then making a very deep tree (via Javascript) should be a good test case. 如果它是基于树的,那么制作一个非常深的树(通过Javascript)应该是一个很好的测试用例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM