JavaScript >> Javascript 文檔 >  >> JavaScript

為什麼 getElementsByTagName() 比 querySelectorAll() 快?

昨天,Yahoo 和 SoundManager 的創建者 Scott Schiller 在 Twitter 上表達了一些關於為什麼 getElementsByTagName("a") 的困惑 比 querySelectorAll("a") 快 在幾乎所有瀏覽器中。有一個比較兩者的 JSPerf 測試,您可以發現速度比較相當明顯。在我現在使用的瀏覽器中,Windows XP 上的 Firefox 3.6.8,querySelectorAll("a")getElementsByTagName("a") 慢了令人震驚的 98% .我、Scott 和 YUI 團隊成員 Ryan Grove 在 Twitter 上討論了為什麼會這樣,以及這真的是多麼令人失望但並不出人意料。我想我會跟進一個更長的描述,說明為什麼會發生這種情況以及為什麼它可能不會有太大變化。

在深入細節之前,這兩種方法之間有一個非常重要的區別,並不是一種只接受標籤名稱而另一種接受完整的 CSS 選擇器。最大的區別在於返回值:getElementsByTagName() 方法返回一個實時 NodeListquerySelectorAll() 返回靜態 NodeList .理解這一點非常重要。

實時節點列表

這是文檔對像模型的主要問題之一。 NodeList 對象(也就是 HTMLCollection HTML DOM 中的對象)是一種特殊類型的對象。 DOM Level 3 規範說關於 HTMLCollection 對象:

getElementsByTagName() 方法返回這些實時元素集合之一,這些元素在文檔更改時自動更新。因此,以下實際上是一個無限循環:

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

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

發生無限循環是因為 divs.length 每次通過循環重新計算。由於循環的每次迭代都在添加一個新的 <div> ,表示 divs.length 每次循環都會遞增,所以 i ,也是遞增的,永遠追不上,永遠不會觸發終端條件。

這些實時集合似乎是個壞主意,但它們的存在是為了使相同的對象能夠用於 document.images , document.forms ,以及其他在瀏覽器中變得司空見慣的類似 pre-DOM 集合。

靜態節點列表

querySelectorAll() 方法不同,因為它是一個靜態的 NodeList 而不是一個活的。這在 Selectors API 規範中有說明:

所以即使 querySelectorAll() 的返回值 與 getElementsByTagName() 返回的方法和行為相同 ,它們實際上是非常不同的。在前一種情況下,NodeList 是調用方法時文檔狀態的快照,而後一種情況將始終與文檔的當前狀態保持同步。這*不是*無限循環:

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

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

在這種情況下沒有無限循環。 divs.length 的值 永遠不會改變,所以循環本質上會使 <div> 的數量增加一倍 文檔中的元素,然後退出。

那麼為什麼實時 NodeLists 更快?

直播NodeList 瀏覽器可以更快地創建和返回對象,因為它們不必在靜態 NodeList 時預先擁有所有信息 s 需要從一開始就擁有他們的所有數據。為了強調這一點,WebKit 源代碼對於每種類型的 NodeList 都有一個單獨的源文件 :DynamicNodeList.cpp 和 StaticNodeList.cpp。這兩種對像類型的創建方式非常不同。

DynamicNodeList 對像是通過在緩存中註冊其存在來創建的。本質上,聽到創建一個新的 DynamicNodeList 非常小,因為它不需要預先做任何工作。每當 DynamicNodeList 被訪問時,它必須查詢文檔的更改,如 length 所示 屬性和 item() 方法(與使用括號表示法相同)。

將此與 StaticNodeList 進行比較 對象,其實例在另一個文件中創建,然後用循環內的所有數據填充。與使用 DynamicNodeList 相比,在文檔上運行查詢的前期成本要高得多 實例。

如果您查看實際為 querySelectorAll() 創建返回值的 WebKit 源代碼 ,您會看到使用循環來獲取每個結果並構建 NodeList 最終返回。

結論

getElementsByTagName() 的真正原因 比 querySelectorAll() 快 是因為live和static的區別NodeList 對象。雖然我確信有辦法對此進行優化,但沒有為實時 NodeList 做前期工作 通常總是比創建靜態 NodeList 的所有工作要快 .確定使用哪種方法在很大程度上取決於您要執行的操作。如果您只是按標籤名稱搜索元素並且不需要快照,那麼 getElementsByTagName() 應該使用;如果您確實需要結果快照或者您正在執行更複雜的 CSS 查詢,那麼 querySelectorAll() 應該使用。


Tutorial JavaScript 教程
  1. Web 開發問答 #1:回調、LESS 和浮點數

  2. React – 如何從 React.Component 中剝離頂級 div

  3. Azure 靜態 Web 應用非常棒

  4. 反應 101 教程

  5. Google Sheets API v4 寫入數據

  6. 我使用 Node 為我的上一篇 dev.to 文章生成了內容😎

  7. XHR 不會在 IE 中的 beforeunload 事件上發送到服務器

  1. 使用 React 顯示和隱藏密碼

  2. 添加一個只有 javascript 而不是 URL 的書籤

  3. React 中的 Draggin 和 Droppin

  4. 將 React.createClass 轉換為 ES6 類

  5. React 的最佳 UI 庫和工具包

  6. 創建藍框區域以組織表單

  7. 初學者的 React Context – 完整指南 (2021)

  1. bootstrap.min.css 和 bootstrap.min.js 的 404 錯誤

  2. 使用 chart.js 庫可視化數據(教程)

  3. 如何使用 RxJS 和 React Hooks

  4. LocalStack:來自您的機器的 AWS 的強大功能