HTMLCollection可以使用for...of (Symbol.iterator)进行迭代吗?

8

DOM4使 NodeList可迭代:

interface <i>NodeList</i> {
  getter <a rel="noreferrer" href="https://www.w3.org/TR/dom/#node">Node</a>? <a rel="noreferrer" href="https://www.w3.org/TR/dom/#dom-nodelist-item">item</a>(unsigned long <i>index</i>);
  readonly attribute unsigned long <a rel="noreferrer" href="https://www.w3.org/TR/dom/#dom-nodelist-length">length</a>;
  iterable<<a rel="noreferrer" href="https://www.w3.org/TR/dom/#node">Node</a>>;
};

根据 WebIDL,这意味着:
实现声明为可迭代的接口的对象支持被迭代以获取一系列值。
注意:在 ECMAScript 语言绑定中,一个可迭代的接口将在其接口原型对象上具有“entries”、“forEach”、“keys”、“values”和 @@iterator 属性。
因此,以下操作是可能的:
for (var el of document.querySelectorAll(selector)) ...

我注意到同样适用于HTML集合,在Firefox和Chrome上都是如此:

for (var el of document.getElementsByTagName(tag)) ...

事实上,我得到
HTMLCollection.prototype[Symbol.iterator] === [][Symbol.iterator]; // true

然而,HTMLCollection 并未被定义为可迭代对象:
interface <i>HTMLCollection</i> {
  readonly attribute unsigned long <a rel="noreferrer" href="https://www.w3.org/TR/dom/#dom-htmlcollection-length">length</a>;
  getter <a rel="noreferrer" href="https://www.w3.org/TR/dom/#element">Element</a>? <a rel="noreferrer" href="https://www.w3.org/TR/dom/#dom-htmlcollection-item">item</a>(unsigned long <i>index</i>);
  getter <a rel="noreferrer" href="https://www.w3.org/TR/dom/#element">Element</a>? <a rel="noreferrer" href="https://www.w3.org/TR/dom/#dom-htmlcollection-nameditem">namedItem</a>(DOMString <i>name</i>);
};

我还查看了WHATWG DOM规范,它也不可迭代。那么,这种行为是标准的还是不标准的?HTMLCollection应该在原型中有一个@@iterator吗?

https://dev59.com/S10Z5IYBdhLWcg3wlRMV#31574921 - zer00ne
2个回答

7

我找到了,它在WebIDL中解释:

如果接口具有以下任何一项:
- 可迭代声明 - 索引属性getter和一个名为“length”的整数类型 属性 - 类map声明 - 类set声明 那么必须存在一个名为@@iterator的属性,其属性为{ [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true },其值为函数对象。 [...]
如果接口定义了一个索引属性getter,那么Function对象就是%ArrayProto_values%
在这种情况下,HTMLCollections具有索引属性获取器:
getter <a rel="nofollow noreferrer" href="https://www.w3.org/TR/dom/#element">Element</a>? <a rel="nofollow noreferrer" href="https://www.w3.org/TR/dom/#dom-htmlcollection-item">item</a>(unsigned long <i>index</i>);

还有一个名为“length”的整数类型属性:

readonly attribute unsigned long <a rel="nofollow noreferrer" href="https://www.w3.org/TR/dom/#dom-htmlcollection-length">length</a>;

因此,是的,它应该可以工作。事实上,即使NodeLists没有声明为可迭代对象,它们也可以使用,但是它们将没有entriesforEachkeysvalues属性。正如@lonesomeday所提到的,很可能HTMLCollection没有被定义为可迭代对象,因为添加这些方法将不兼容向后,由于namedItem Getter接受任意字符串。

我猜这可能是由于HTMLCollection上的namedItem方法或者语句“注意:Elements是表示元素集合的更好解决方案。HTMLCollection是我们无法摆脱的历史遗物。”所导致的。 - lonesomeday
2
@lonesomeday 是的,看起来是这样,因为 namedItem 接受一个字符串,添加像 forEach 这样的属性可能会破坏它。 - Oriol

0
在JavaScript中,几乎所有具有可迭代结构的内容都可以通过一些迭代引擎操作进行迭代,例如:for..of...spread
如果返回一个存储了@@iterator符号的属性的迭代器对象,则任何东西都是可迭代的。在这种情况下,HTMLCollection返回这样的对象。
如果迭代器本身具有next() {method}:,以及另外两个可选方法,则迭代器本身就是可计数的。
return() {method}: stops iterator and returns IteratorResult
throw() {method}: signals error and returns IteratorResult

这是一个完整的自定义对象示例,可迭代。

    const calculations = {
      counting: 1,
      [Symbol.iterator]: function(){ return this; },
      next: function(){

        if(this.counting <= 3){

          return {
            value: this.counting++,
            done: false
          }
        }

        return { value: this.counting, done: true}
      }
    };

const data = ...calculations; // [1,2,3]

所以在你的情况下,只要 HTMLCollection 返回一个合适的迭代器,你就可以应用 for..of...spread,但如果你关心的是它是否符合规范,那么我必须告诉你,我还没有计算机科学学士学位 :P


谢谢,我已经知道了 @@iterator,我的担忧是 HTMLCollection 是否应该返回一个适当的迭代器。 - Oriol
@Oriol 我认为这取决于实现。HTMLCollection接口没有声明迭代器,但在实现时可以包含另一个接口。在这种情况下,HTMLCollection和NodeList是集合的表示形式,确实是可迭代的。 - Rosmarine Popcorn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接