一次性理解 NodeList 和 HTMLCollection

423 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

NodeList

NodeList 对象是节点的集合,它是一个类似数组的对象,并不是真正的数组

可以通过以下方法获取 NodeList 对象:

  • Node.childNodes 属性返回
  • document.querySelectorAll() 方法返回
document.body.childNodes instanceof NodeList
document.querySelectorAll('body') instanceof NodeList

// instanceof NodeList 判断是否是 NodeList 对象

静态 or 动态

在大多数情况下,NodeList 是一个静态集合,不被文档中节点树的变化影响,比如 document.querySelectorAll 的返回就是静态的 NodeList

但是某些情况下,NodeList 是一个实时集合,会随着文档中节点树的变化而变化:

let parent = document.getElementById('parent');
let child_nodes = parent.childNodes;
console.log(child_nodes.length); // 输出:2

parent.appendChild(document.createElement('div'));
console.log(child_nodes.length); // 输出:3

属性

  • length:包含的节点数量

方法

除 IE 浏览器只支持 item() 方法外,其他主流浏览器对下述方法是全支持的。

  • forEach()
    • 遍历所有包含的节点,使用方式和 Array.forEach() 一致
  • item()
    • 根据索引返回某个元素,nodeList.item[i] 等价于 nodeList[i]
    • nodeList.item[i] 索引越界返回 null
    • nodeList[i] 索引越界返回 undefined
  • entries()
    • 返回一个 iterator,允许代码遍历集合中包含的所有键/值对
    • 键是从 0 开始的数字,值是节点
  • keys()
    • 返回一个 iterator,允许代码遍历集合中包含的键/值对的所有键
  • values()
    • 返回一个 iterator,允许代码遍历集合中包含的键/值对的所有值

上述五个方法中,forEach() 可以快速遍历 NodeList 对象(但是注意,它只是类数组对象,如 Array.map 的其他数组方法是不支持的),item() 可以根据索引获取具体的节点元素,entries()keys()values() 都返回了一个迭代器对象,可以通过 for...of 循环遍历。

HTMLCollection

HTMLCollection 对象表示一个包含了元素(元素顺序为文档流中的顺序)的通用集合。同样,它是一个类似数组的对象,并不是真正的数组

可以通过以下方法获取 HTMLCollection 对象:

  • document.getElementsByTagName() 方法返回
document.getElementsByTagName('body') instanceof HTMLCollection

// instanceof HTMLCollection 判断是否是 HTMLCollection 对象

动态

HTMLCollection 是即时更新的:当其所包含的文档结构发生改变时,它会自动更新。因此,对数据的快照进行操作是容易存在问题,可以通过创建副本来操作 DOM 数据。

属性

  • length:包含的元素数量

方法

  • item()
    • 根据索引返回某个元素,collection.item[i] 等价于 collection[i]
    • collection.item[i] 索引越界返回 null
    • collection[i] 索引越界返回 undefined
  • namedItem()
    • 根据 id 返回某个元素,collection.namedItem[id] 等价于 collection[id]
    • 若匹配不到元素,则根据 name 属性来匹配元素
    • 若 id 和 name 都匹配不到,collection.namedItem[id] 返回 null
    • 若 id 和 name 都匹配不到,collection[id] 返回 undefined

其他

为什么 NodeList 不是数组?

HTMLCollection 在这方面的概念与 NodeList 一致,此处即用 NodeList 举例


JavaScript 的继承机制是基于原型的。数组元素的数组方法是从 Array.prototype 这个对象继承来的:newArray --> Array.prototype --> Object.prototype --> null

但是,NodeList 对象的原型链是这样的:newNodeList --> NodeList.prototype --> Object.prototype --> null,并不会继承 Array.prototype 对象的方法。

解决方案:如何支持数组方法?

拓展原型

可以将 Array.prototype 上的方法添加到 NodeList.prototype 上,但是拓展 DOM 对象的原型是非常危险的,并不推荐。

我们无法判断某些属性是否还不是某些 DOM 的一部分。如果是,它可以被覆盖吗?还是在尝试这样做时会抛出错误?请记住,它是一个宿主对象!如果我们可以悄悄地覆盖它,它将如何影响 DOM 的其他部分?一切都会按预期工作吗?如果在此类浏览器的一个版本中一切正常,是否可以保证下一版本不会引入同名属性?问题清单还在继续。

Array.from

const nodeList = document.querySelectorAll('body')
Array.from(nodeList)

applycall 和 bind

const nodeList = document.querySelectorAll('body')
Array.apply(null, nodeList)

拓展运算符

const nodeList = [...document.querySelectorAll('body')]

DOM 标准

根据 DOM 标准,HTMLCollection 对象和 NodeList 对象都是所谓的 Old-style collections,它们的概念是一致,都是用来表示节点列表的集合,这种表示默认是动态的,但是在另有说明的情况下,也可以是静态的(如 document.querySelectorAll() 方法返回的 NodeList 就是静态的)。

因此,关于静态和动态的概念需要格外留意,具体使用具体学习,以避免意外和错误的发生。