详解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')
打印结果

我们发现返回的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?),如图:

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));

打印结果:

keys():

返回一个iterator用于遍历NodeList的key:

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

打印结果:

values():

keys()类似,返回一个iterator用于遍历NodeList的value,即html元素:

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

打印结果:

通过对NodeList的研究我们发现,NodeList和Array没有继承关系,但是都有length属性和forEach方法,而且拥有几个特有的方法,主要都是用来遍历和取值用的。

HTMLCollection

认识了NodeList,我们再来认识一下HTMLCollection,同样我们先获取到一个HTMLCollection,在控制台中输入并执行:

document.getElementsByTagName('div')

打印结果:

可以看到得到的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);

打印结果:

我们看到childNodes返回的是第一个div下面的所有DOM节点,包含3个text node(其中两个是换行符),一个子div,一个comment。这证实了我们对NodeList的猜想。

我们再看一下HTMLCollection,执行:

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

打印结果:

只包含了子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(死的?静态的?),关于这个话题,之后的文章再详细分析。

原文链接:segmentfault.com

上一篇:[phaser3学习]使用phaser3做一款飞刀小游戏
下一篇:JavaScript创建对象的四种方式

相关推荐

  • (JavaScript) Array的tips

    1. Array.prototype.push() 像数组一样使用对象: 尽管 obj 不是数组,但是 push 方法成功地使 obj 的 length 属性增长了,就像我们处理一个实际的数...

    2 年前
  • 集合之ArrayList的介绍和常用方法

    package com.itheima.demo01.List; import java.util.ArrayList; import java.util.Iterator; import java...

    1 年前
  • 转行学前端的第 33 天 : 了解 ECMAScript Array 实例对象方法

    我是小又又,住在武汉,做了两年新媒体,准备用 6 个月时间转行前端。 今日学习目标 昨天主要是基于搜索来基础学习 Array 对象,今天准备学习一下Array实例对象的方法 ,又是适合学习的一天...

    25 天前
  • 转行学前端的第 32 天 : 了解 ECMAScript Array 对象基础

    我是小又又,住在武汉,做了两年新媒体,准备用 6 个月时间转行前端。 今日学习目标 昨天基于搜索来仔细学习 Object 对象相关的知识。今天主要是基于搜索来基础学习 Array 对象属性和Ar...

    1 个月前
  • 转换一个htmlcollection数组最有效的方式

    Tom(https://stackoverflow.com/users/20/tom)提出了一个问题:Most efficient way to convert an HTMLCollection t...

    2 年前
  • 详谈js中数组(array)和对象(object)的区别

    •object 类型: ◦ 创建方式: •array类型 ◦ 创建方式: • 区别和不解 ◦ 比如有一个数组a=1,2,3,4,还有一个对象a={0:1,1:2,2:3,3:4},然...

    3 年前
  • 详解数组Array.sort()排序的方法

    JavaScript中数组的sort()方法主要用于对数组的元素进行排序。其中,sort()方法有一个可选参数。但是,此参数必须是函数。 数组在调用sort()方法时,如果没有传参将按字母顺序(字符编...

    3 年前
  • 聊聊 Array 中的坑

    原文:https://jakearchibald.com/201...(https://jakearchibald.com/2017/arrayssymbolsrealms/) 翻译:疯狂的技术宅...

    1 年前
  • 简单实现 VUE 中 MVVM - step6 - Array

    看这篇之前,如果没看过之前的文章先移步看 1. 简单实现 VUE 中 MVVM step1 defineProperty(http://blog.acohome.cn/2018/04/11/v...

    2 年前
  • 理解Array.prototype.slice.call()

    晚上看张鑫旭老师的一篇文章,发现.splice.call()这样的用法,很感兴趣,因为昨天刚写完一篇关于call方法的博客《一文读懂js中的call和apply》(以下简称“《call》”),想一探究...

    3 个月前

官方社区

扫码加入 JavaScript 社区