為什么getElementsByTagName()要比querySelectorAll()快很多?


相信很多人第一反應當然是這樣的了,querySelectorAll的參數是一個css selector,這一步還需要處理呢,肯定會比直接getElementsByTagName要慢了。具體jsperf上有相關的對比,確實getElementsByTagName要比querySelectorAll要快很多,這里不放具體鏈接了,因為這個網站掛了很長一段時間了已經。

廢話不說,直接看原文Why is getElementsByTagName() faster than querySelectorAll()?

這里大概翻譯一下,覺得不妥的地方還望指教。

 

首先這兩個方法有個非常明顯的區別,一個接受一個tag name的參數,另外一個接受一個css selector。這兩個方法最大的不同點是,getElementsByTagName返回的是一個動態的NodeList而querySelectorAll返回的是一個靜態的NodeList。

動態的NodeLists

NodeList和HTMLCollection是兩個特別的對象類型,3級DOM中是這樣定義HTMLCollection:

  NodeList和NamedNodeMap在DOM中都是動態的,也就是說,改變這些dom結構都會影響到這些NodeList和NamedNodeMap。例如:如果獲取一個有子節點的NodeList,然后增加或者刪除子節點,這個NodeList也會動態的改變。同樣的,在一個tree結構中改變一個node會影響到所有引用這個node的NodeList和NamedNodeMap對象。

getElementsByTagName方法即返回的就是這樣的一個NodeList,看下面代碼

var divs = document.getElementsByTagName("div"),
    i=0;

while(i < divs.length){
    document.body.appendChild(document.createElement("div"));
    i++;
}

這是一個死循環,因為divs.length在每次循環都會+1。

也許這樣的動態設計是一個缺陷,但是開發者已經習慣了這種動態的NodeList。

 

靜態的NodeLists

querySelectorAll返回的是一個靜態的NodeList,下面是 Selectors API

  querySelectorAll返回的是一個靜態的NodeList。改變一個相關node不會改變這個靜態NodeList對象。也就是說這個對象必須在獲取的時候就被創建出來。


所以即使querySelectorAll返回的結果和getElementsByTagName是一樣的,但是他們還是有着本質的區別。在動態NodeList里return的是一個指針,而靜態的NodeList返回時當前的node所有信息。

所以下面這個循環就不是一個死循環

var divs = document.querySelectorAll("div"),
    i=0;

while(i < divs.length){
    document.body.appendChild(document.createElement("div"));
    i++;
}

因為在這段代碼里,divs.length不會動態的改變。

 

所以為什么動態的NodeLists更快呢?

動態的NodeList返回的更快是因為不需要獲取這個節點的所有信息,而靜態的需要。為了證明這個觀點,可以看一下webkit源碼DynamicNodeList.cppStaticNodeList.cpp,這兩種對象創建的方式不一樣。

getElementsByTagName創建的過程不需要做任何操作,只需要返回一個指針即可(此處可以理解為shadow clone)。而querySelectorAll會循環遍歷所有的的結果,然后創建一個新的NodeList(此處可以理解為deep clone)

 

結論

getElementsByTagName比querySelectorAll快的原因是因為動態和靜態NodeList的區別。實際在用的過程中取決於你將要做什么,如果你只是查詢一個tag name建議用getElementsByTagName,如果查詢的是一個css selector建議使用querySelectorAll。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM