在JavaScript ES6中,可迭代和迭代器之间有什么区别? [英] In JavaScript ES6, what is the difference between an iterable and iterator?

查看:47
本文介绍了在JavaScript ES6中,可迭代和迭代器之间有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

可迭代与迭代器相同还是不同?

Is an iterable the same as an iterator, or are they different?

在规范中, 是一个对象,例如obj,使得obj[Symbol.iterator]引用一个函数,以便在调用时返回具有next方法的对象,该方法可以返回{value: ___, done: ___}对象:

It seems, from the specifications, an iterable is an object, say, obj, such that obj[Symbol.iterator] refers to a function, so that when invoked, returns an object that has a next method that can return a {value: ___, done: ___} object:

function foo() {
    let i = 0;
    const wah = {
        next: function() {
            if (i <= 2) return { value: (1 + 2 * i++), done: false }
            else return { value: undefined, done: true }
        }
    };
    return wah;     // wah is iterator
}

let bar = {}        // bar is iterable

bar[Symbol.iterator] = foo;

console.log([...bar]);             // [1, 3, 5]   
for (a of bar) console.log(a);     // 1 3 5 (in three lines)

因此在上面的代码中,bar是可迭代的,而wah是迭代器,而next()是迭代器接口.

So in the code above, bar is the iterable, and wah is the iterator, and the next() is the iterator interface.

因此,可迭代和迭代器是不同的东西.

So, iterable and iterator are different things.

现在,在生成器和迭代器的常见示例中:

Now, however, in a common example of generator and iterator:

function* gen1() {
    yield 1;
    yield 3;
    yield 5;
}

const iter1 = gen1();

console.log([...iter1]);                           // [1, 3, 5]
for (a of iter1) console.log(a);                   // nothing

const iter2 = gen1();
for (a of iter2) console.log(a);                   // 1 3 5 (in three lines)

console.log(iter1[Symbol.iterator]() === iter1);   // true

在上述情况下,gen1是生成器,iter1是迭代器,并且iter1.next()将做适当的工作.但是iter1[Symbol.iterator]确实提供了一个函数,该函数在被调用时会返回作为迭代器的iter1.那么iter1在这种情况下既是可迭代的又是迭代器?

In the case above, gen1 is the generator, and iter1 is the iterator, and iter1.next() will do the proper job. But iter1[Symbol.iterator] does give a function that, when invoked, gives back iter1, which is an iterator. So iter1 is both an iterable and iterator in this case?

此外,iter1与上面的示例1不同,因为示例1中的可迭代项可以使用[...bar]给出[1, 3, 5]所需的次数,而iter1是可迭代项,但是由于它返回了自身,每次都是相同的迭代器,只会给出[1, 3, 5]一次.

Besides, iter1 is different from the example 1 above, because the iterable in example 1 can give [1, 3, 5] as many times as wanted using [...bar], while iter1 is an iterable, but since it returns itself, which is the same iterator every time, will only give [1, 3, 5] once.

因此,对于一个可迭代的bar,我们可以说[...bar]可以给出结果[1, 3, 5]多少次-答案取决于.并且可迭代与迭代器相同吗?答案是,它们是不同的东西,但是当可迭代对象将自身用作迭代器时,它们可以是相同的.正确吗?

So we can say, for an iterable bar, how many times can [...bar] give the result [1, 3, 5] -- and the answer is, it depends. And is iterable the same as an iterator? And the answer is, they are different things, but they can be the same, when the iterable uses itself as the iterator. Is that correct?

推荐答案

是的, iterables iterators 是不同的东西,但是大多数迭代器(包括您所使用的所有迭代器)从JavaScript本身获取,例如从Array.prototypekeysvalues方法或生成器函数的生成器)继承自

Yes, iterables and iterators are different things, but most iterators (including all of the ones you get from JavaScript itself, such as from the keys or values methods on Array.prototype or generators from generator functions) inherit from the %IteratorPrototype% object, which has a Symbol.iterator method like this:

[Symbol.iterator]() {
    return this;
}

结果是所有标准迭代器也是可迭代的.这样一来,您可以直接使用它们,也可以在for-of循环中使用它们(诸如此类,它们需要迭代器,而不是迭代器).

The result is that all standard iterators are also iterables. That's so you can use them directly, or use them in for-of loops and such (which expect iterables, not iterators).

考虑数组的keys方法:它返回一个数组迭代器,该迭代器访问数组的键(其索引为数字).请注意,它返回一个 iterator .但是它的常见用法是:

Consider the keys method of arrays: It returns an array iterator that visits the array's keys (its indexes, as numbers). Note that it returns an iterator. But a common use of it is:

for (const index of someArray.keys()) {
    // ...
}

for-of需要一个 iterable ,而不是 iterator ,那为什么行得通呢?

for-of takes an iterable, not an iterator, so why does that work?

之所以有效,是因为迭代器也是可迭代的. Symbol.iterator仅返回this.

It works because the iterator is also iterable; Symbol.iterator just returns this.

这是我在本书第6章中使用的示例:如果您想遍历所有条目但跳过第一个条目,并且不想使用slice来分割子集,则可以获取迭代器,读取第一个值,然后切换到for-of循环:

Here's an example I use in Chapter 6 of my book: If you wanted to loop over all entries but skip the first one and you didn't want to use slice to slice off the subset, you can get the iterator, read the first value, then hand off to a for-of loop:

const a = ["one", "two", "three", "four"];
const it = a[Symbol.iterator]();
// Skip the first one
it.next();
// Loop through the rest
for (const value of it) {
    console.log(value);
}

请注意,这就是所有 standard 迭代器.有时人们会显示诸如此类的手动编码迭代器的示例:

Note that this is all standard iterators. Sometime people show examples of manually-coded iterators like this:

function range(start, end) {
    let value = start;
    let inc = start < end ? 1 : -1;
    return {
        next() {
            const done = value == end;
            const result = {done, value};
            if (!done) {
                value += inc;
            }
            return result;
        }
    };
}

// Works when used directly
const it = range(1, 5);
let result;
while (!(result = it.next()).done) {
    console.log(result.value);
}

// Fails when an iterable is expected
try {
    for (const value of range(1, 5)) {
        console.log(value);
    }
} catch (e) {
    console.error(e.message);
}

range返回的迭代器没有 可迭代,因此当我们尝试将其与for-of一起使用时,它将失败.

The iterator returned by range there is not an iterable, so it fails when we try to use it with for-of.

要使其可迭代,我们需要:

To make it iterable, we'd need to either:

  1. 在上述答案的开头添加Symbol.iterator方法,或者
  2. 使其继承自已具有该方法的%IteratorPrototype%
  1. Add the Symbol.iterator method at the beginning of the answer above to it, or
  2. Make it inherit from %IteratorPrototype%, which already has that method

遗憾的是,TC39决定不提供直接方法来获取%IteratorPrototype%对象.有一种间接方法(从数组中获取迭代器,然后获取其原型(定义为%IteratorPrototype%)),但这很痛苦.

Sadly, TC39 decided not to provide a direct way to get the %IteratorPrototype% object. There's an indirect way (getting an iterator from an array, then taking its prototype, which is defined to be %IteratorPrototype%), but it's a pain.

但是仍然不需要像这样手动编写迭代器.只需使用一个生成器函数,因为它返回的生成器是可迭代的:

But there's no need to write iterators manually like that anyway; just use a generator function, since the generator it returns is iterable:

function* range(start, end) {
    let value = start;
    let inc = start < end ? 1 : -1;
    while (value !== end) {
        yield value;
        value += inc;
    }
}

// Works when used directly
const it = range(1, 5);
let result;
while (!(result = it.next()).done) {
    console.log(result.value);
}

// Also works when an iterable is expected
for (const value of range(1, 5)) {
    console.log(value);
}

相比之下,并不是所有的迭代器都是迭代器.数组是可迭代的,但不是迭代器.字符串,地图和集合也是如此.

In contrast, not all iterables are iterators. Arrays are iterable, but not iterators. So are strings, Maps, and Sets.

这篇关于在JavaScript ES6中,可迭代和迭代器之间有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆