NodeList、HTMLCollection 和 Array詳解

Array,NodeList, HTMLCollection這三個概念和它們之間的關係有很多做了幾年前端的同學都搞不清楚,經常遇到但是又感覺很陌生,剪不斷理還亂的感覺。今天咱們就來理清這三個東西。

對於Array大家差不多都能弄明白,但是HTMLCollectio、NodeList和Array的關係好像總是很曖昧,有一點像但是又不那麼像,可能是我比較笨,但是真的被它們弄得很頭疼啊,所以今天下決心必須弄懂它們。

咱們先不管那麼多概念和定義,先來看看這三個東西到底長什麼樣。咱們先創建一個html文件,裏面就放三個嵌套的div:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div class="test-div">
        div1
        <div class="test-div">
            div2
            <div class="test-div">
                div3
            </div>
        </div>
    </div>
</body>
</html>

NodeList

首先讓我們來研究一下NodeList,在瀏覽器中打開這個html文件,打開控制檯輸入:

document.querySelectorAll('div')
打印結果

image

我們發現返回的NodeList中包含這三個div。展開NodeList的__proto__屬性後發現,NodeList繼承於一個NodeList對象,而這個NodeList對象又繼承於Object對象。

NodeList除了length屬性外還有其他5個方法(method),分別是entries, forEach, item, keys, values,這五個方法都是幹什麼用的呢?用一遍就知道:

entries():

調用entries方法會返回一個iterator(迭代器),關於iterator/iterable可以參見MDN,簡單點說就是返回了一個可以遍歷的對象,而這個對象實現了iterable protocal,所以需要用for...of遍歷,所以我們可以:

var divs = document.querySelectorAll('div');
for(var item of divs.entries()){
    console.log(item);
}

結果返回了三個包含三個div對象數組(爲什麼不是三個key-value pair?),如圖:

image

forEach():

forEach的用法和Array的forEach用法一樣,都是用於遍歷集合元素:

var divs = document.querySelectorAll('div');
divs.forEach(function (el, index, list) {
    console.log(el);
});
item():

item()用於從NodeList中獲取單個節點元素:

var divs = document.querySelectorAll('div');
console.log(divs.item(0));

打印結果:
image

keys():

返回一個iterator用於遍歷NodeList的key:

var divs = document.querySelectorAll('div');
for (var key of list.keys()) {
    console.log(key);
}

打印結果:
image

values():

keys()類似,返回一個iterator用於遍歷NodeList的value,即html元素:

var divs = document.querySelectorAll('div');
for (var value of divs.values()) {
    console.log(value);
}

打印結果:
image

通過對NodeList的研究我們發現,NodeList和Array沒有繼承關係,但是都有length屬性和forEach方法,而且擁有幾個特有的方法,主要都是用來遍歷和取值用的。

HTMLCollection

認識了NodeList,我們再來認識一下HTMLCollection,同樣我們先獲取到一個HTMLCollection,在控制檯中輸入並執行:

document.getElementsByTagName('div')

打印結果:

image

可以看到得到的HTMLCollection繼承於一個HTMLCollection對象,而HTMLCollection又直接繼承於Object對象,所以它和NodeList是平級的。HTMLCollection和NodeList一樣包含了查詢得到的html元素,length屬性和item方法,但沒有NodeList的entries, forEach, keys, values這四個方法,但是又多了一個namedItem(根據id和name篩選元素)方法…

看到了NodeList和HTMLCollection這兩個傢伙的真容,我們很好奇這兩個有很多相似又相互獨立的傢伙是怎麼被髮明出來的呢?什麼情況下得到的是NodeList,什麼情況是HTMLCollection呢?

MDN上是這麼介紹HTMLCollection的:

Note: This interface is called HTMLCollection for historical reasons (before the modern DOM, collections implementing this interface could only have HTML elements as their items).

翻譯一下就是:
之所以叫它HTMLCollection是因爲某些歷史原因,在新一代DOM出現之前,實現HTMLCollection這個接口的集合只包含HTML元素,所以命名爲HTMLCollection。

我們知道DOM節點(node)不光包含HTML元素,還包含text node(字符節點)和comment(註釋),既然HTMLCollection只包含HTML元素,那NodeList是不是會包含所有類型的DOM節點呢,我們來試驗一下,先寫一段html:

<div class="parent">
    this is patent content
    <div class="child">
        this is child content
    </div>
    <!-- this is comment -->
</div>

然後執行:

var parent = document.querySelector('.parent');
console.log(parent.childNodes);

打印結果:

image

我們看到childNodes返回的是第一個div下面的所有DOM節點,包含3個text node(其中兩個是換行符),一個子div,一個comment。這證實了我們對NodeList的猜想。

我們再看一下HTMLCollection,執行:

var parent = document.querySelector('.parent');
console.log(parent.children);

打印結果:

image

只包含了子div,也驗證了MDN上的說法。

至於parent即有childNodes屬性,又有children屬性呢?

因爲parent即是一個Node對象(擁有childNodes屬性),又因爲它有子元素所以它又是一個ParentNode對象(擁有children屬性)。

至此,我們對NodeList和HTMLCollection應該有一個比較全面的認識,總結一下就是HTMLCollection是比較早期的模型,只能包含HTML元素,早期就有的接口如document.getElementsByClassName, document.getElementsByTagName返回的就是HTMLCollection。NodeList是比較新的模型,相比HTMLCollection更加完善,不光有HTML元素,還有text節點和comment。比較新的接口如document.querySelectorAll返回的就是NodeList。

關於NodeList,HTMLCollection和Array的關係,就是長得像,有個別相似的功能,但是是完全不一樣的東西。

當然關於HTMLCollection和NodeList的故事還沒有講完,因爲它們有時候是live(活的?動態的?),有時候是not live(死的?靜態的?),關於這個話題,之後的文章再詳細分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章