[英]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友好地指出了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>
它應該不難測試。
如果它是基於樹的,那么制作一個非常深的樹(通過Javascript)應該是一個很好的測試用例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.