繁体   English   中英

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

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

DOM是否有一个元素的哈希表,其键是元素'ID?
我想知道document.getElementById查找哈希表或遍历整个树。
这种行为在浏览器中有所不同吗

我知道Firefox和WebKit DOM实现,它们都使用哈希表来查找元素,深入研究它们的来源,你可以看看内部实现:

如果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实现, nsDocument.cpp

实现可以自由地做任何他们喜欢的事情,但由于id被定义为唯一值,因此使用哈希映射或类似的查找机制而不是遍历似乎是明智的。 但是,当您进入构建具有许多(有时相互矛盾的)命令的复杂Web浏览器的管道时,外部看起来似乎是明智的。

我做了一个简单但非常简单的测试(请参阅答案末尾的页面)。 非常简单,尤其是因为我们不知道浏览器不会缓存以前的结果。

Chrome 4.1.249.1059报告:

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

因此,ID比标签名称快得多。

IE7报告:

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

标签名称比ID快得多(但请记住IE7有一个破坏的getElementById概念 ;这在IE8中得到修复)。

IE8 (在不同的机器上 ,不比较绝对值,我们正在查看测试的浏览器中的差异)报告:

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

所以和IE7一样。

Firefox 3.6.3报告:

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

所以它并不在乎(重复请求; 再次 ,它可能是缓存)。

Opera 10.5.1报道:

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

ID比标签名称快得多。

按照你的意愿制作这些结果。 需要一个更复杂的测试用例来真正推断出这些机制。 当然,至少在其中两个案例中(Firefox和Chrome), 我们可以看看源代码 CMS友好地指出WebKitFirefox的实现(并且看着它,我对缓存的怀疑可能是 )。

测试页面:

<!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>

HTML规范中未定义具体实现,因此可以轻松地将浏览器更改为浏览器。 例如IE文档说明

返回对具有指定ID或NAME属性值的第一个对象的引用。

所以我很想说它会进行搜索(或者只是在哈希冲突的情况下抛出元素)。

编辑还要记住,还有其他数据结构(如树)允许访问时间介于常量和线性之间。

它应该不难测试。

如果它是基于树的,那么制作一个非常深的树(通过Javascript)应该是一个很好的测试用例。

暂无
暂无

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

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