Поиски Javascript getElementById - хешируют карту или рекурсивный обход дерева?

DOM имеет хеш-таблицу элементов, ключи которых являются идентификаторами элементов?
Я хочу знать если document.getElementById ищет хеш-таблицу или пересекает все дерево.
Действительно ли это поведение отличается через браузеры?

33
задан Kobi 26 April 2010 в 06:05
поделиться

4 ответа

Реализации вольны делать все, что им нравится, но поскольку id определен как уникальное значение, кажется разумным использовать хэш-карту или аналогичный механизм поиска, а не обход. Однако то, что кажется разумным со стороны, может таковым не являться, когда вы вникаете в тонкости создания сложного веб-браузера с множеством (иногда противоречащих друг другу) императивов.

Я провел простой но очень упрощенный тест (см. страницу в конце ответа). Он очень упрощенный не в последнюю очередь потому, что мы не знаем, что браузеры не кэшируют предыдущие результаты.

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 любезно указывает нам на WebKit и Firefox реализации (и глядя на них, мое подозрение о кэшировании, возможно, было не напрасным).

Тестовая страница:

<!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>
12
ответ дан 27 November 2019 в 18:37
поделиться

Я знаю о реализациях Firefox и WebKit DOM, обе из они используют хэш-таблицу для поиска элементов, копаясь в их источнике, вы можете взглянуть на внутренние реализации:

Реализация 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

24
ответ дан 27 November 2019 в 18:37
поделиться

Проверить это несложно.

Если это дерево, то создание очень глубокого дерева (через Javascript) должно быть хорошим тестом.

0
ответ дан 27 November 2019 в 18:37
поделиться

Конкретная реализация не определена в спецификации HTML , поэтому она может легко варьировать браузер от браузера. Например, документация IE состояния

Возвращает ссылку на первый объект с указанным значением атрибута ID или NAME.

, поэтому мне хотелось бы сказать, что он выполняет поиск (или просто выбрасывает элементы в случае хеш-коллизий).

РЕДАКТИРОВАТЬ Также имейте в виду, что существуют другие структуры данных (например, деревья), которые позволяют использовать время доступа где-то между постоянным и линейным.

2
ответ дан 27 November 2019 в 18:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: