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

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

问题描述

iterable 和 iterator 是一样的还是不同的?

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是iterable,wah是iterator,next()是iterator接口.

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

所以,iterable 和 iterator 是不同的东西.

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 中的可迭代对象可以使用 [1, 3, 5] 任意多次code>[...bar],虽然iter1是一个iterable,但是由于它返回的是自己,每次都是同一个迭代器,所以只会给出[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?

推荐答案

是的,iterablesiterators 是不同的东西,但是大多数迭代器(包括你所有的迭代器)从 JavaScript 本身获取,例如从 Array.prototype 上的 keysvalues 方法或生成器函数的生成器)继承自 %IteratorPrototype% object,其中有一个Symbol.iterator方法如下:

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 方法:它返回一个数组迭代器,该迭代器访问数组的键(其索引,作为数字).请注意,它返回一个迭代器.但它的一个常见用法是:

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

请注意,这是所有 标准 迭代器.有时人们会展示这样的手动编码迭代器示例:

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返回的迭代器not是可迭代的,所以当我们尝试将它与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%,后者已经拥有该方法

遗憾的是,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天全站免登陆