关于iterator:Javascript可迭代的技术定义是什么,你如何测试?

What is the technical definition of a Javascript iterable and how do you test for it?

我一直在实现ES6Set对象的一个有用的子类。对于我的许多新方法,我想接受一个参数,它可以是另一个集合或数组,也可以是我可以迭代的任何东西。我在我的接口中称它为"不可修改的",只在它上面使用.forEach()(对于一个集合或一个数组来说,它工作得很好)。示例代码:

1
2
3
4
5
6
7
8
9
10
11
// remove items in this set that are in the otherIterable
// returns a count of number of items removed
remove(otherIterable) {
    let cnt = 0;
    otherIterable.forEach(item => {
        if (this.delete(item)) {
            ++cnt;
        }
    });
    return cnt;
}

1
2
3
4
5
6
// add all items from some other iterable to this set
addTo(iterable) {
    iterable.forEach(item => {
        this.add(item);
    });
}

但是,我怀疑我可能不支持ES6定义它的方式中的任何ITerable,所以我对JavaScript ITerable的实际定义使用的术语和ES6规范一样感兴趣?

如何在ES6 JavaScript中测试它?

您应该如何迭代一个通用的iterable?

我在ES6规范中发现了这样的短语:

If the parameter iterable is present, it is expected to be an object
that implements an @@iterator method that returns an iterator object
that produces a two element array-like object whose first element is a
value that will be used as a WeakMap key and whose second element is
the value to associate with that key.

但是,这指的是我似乎无法通过该属性名访问的@@iterator method


What is the real definition of a Javascript iterable using the term as the ES6 specification does?

§25.1.1.1定义了"ITerable接口"。

它们是具有返回有效迭代器的Symbol.iterator键方法的对象(而该迭代器又是根据第25.1.1.2条预期其行为正常的对象)。

How do you test for it in ES6 Javascript?

我们不能在不调用@@iterator方法的情况下测试它返回的内容,也不能在不尝试运行它的情况下测试结果是否符合Iterator接口。最好的办法是

1
2
3
function looksIterable(o) {
    return typeof o[Symbol.iterator] =="function";
}

然而,我通常不会测试这个,只是让它失败,当它不是不可测的时候例外。

How should you iterate a generic iterable?

不要使用forEach。(事实上,不要在ES6中的任何地方使用forEach)。

迭代的正确方法是一个for (… of …)循环。它执行所有的ITerability检查(使用抽象的GetIterator操作和运行(甚至关闭)迭代器,并在不可ITerable值上使用时抛出适当的TypeErrors。


符号构造器Symbol.iterator上有一个特殊属性,它的值是,呃,我猜你会说"概念"属性名@@iterator。因此,可以为这样的对象创建迭代器方法:

1
2
3
object[Symbol.iterator] = function* () {
  // do something that makes sense
};

您还可以通过执行以下操作来测试某个对象是否是迭代器

1
if (Symbol.iterator in object)

(也可以检查它是否是一个函数)。现在,这些迭代器函数(Symbol.iterator属性的值)是生成器函数(不是我在示例中编辑的*)。因此,您首先调用函数并保存返回的对象,然后调用.next()来启动它们并获取值。这将得到一个具有valuedone属性的对象。

您也可以让for ... of或"spread"...运算符担心迭代器函数。