简体   繁体   English

在 JavaScript ES6 中,可迭代和迭代器有什么区别?

[英]In JavaScript ES6, what is the difference between an iterable and iterator?

Is an iterable the same as an iterator, or are they different?迭代器与迭代器相同还是不同?

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: 从规范来看,iterable 似乎是一个对象,比如obj ,这样obj[Symbol.iterator]指的是一个函数,因此在调用时,返回一个对象,该对象具有可以返回{value: ___, done: ___}next方法{value: ___, done: ___}对象:

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)

So in the code above, bar is the iterable, and wah is the iterator, and the next() is the iterator interface.所以在上面的代码中, bar是迭代器, wah是迭代器, next()是迭代器接口。

So, iterable and iterator are different things.所以,iterable 和 iterator 是不同的东西。

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

In the case above, gen1 is the generator, and iter1 is the iterator, and iter1.next() will do the proper job.在上面的例子中, gen1是生成器, iter1是迭代器, iter1.next()将完成正确的工作。 But iter1[Symbol.iterator] does give a function that, when invoked, gives back iter1 , which is an iterator.但是iter1[Symbol.iterator]确实给出了一个函数,当调用时,它返回iter1 ,它是一个迭代器。 So iter1 is both an iterable and iterator in this case?那么在这种情况下iter1既是可迭代的又是迭代器?

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.此外, iter1与上面的示例 1 不同,因为示例 1 中的可迭代对象可以使用[...bar]多次给出[1, 3, 5] [...bar] ,而iter1是一个可迭代对象,但由于它返回自身,每次都是相同的迭代器,只会给[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.所以我们可以说,对于一个可迭代的bar[...bar]给出多少次结果[1, 3, 5] —— 答案是,这取决于。 And is iterable the same as an iterator? iterable 和迭代器一样吗? 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?那是对的吗?

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:是的,迭代迭代器是不同的东西,但是大多数迭代器(包括你从 JavaScript 本身获得的所有迭代器,例如来自Array.prototype上的keysvalues方法或来自生成器函数的生成器)继承自%IteratorPrototype% 对象,它有一个Symbol.iterator方法,如下所示:

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

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).这样你就可以直接使用它们,或者在for-of循环等中使用它们(需要迭代器,而不是迭代器)。

Consider the keys method of arrays: It returns an array iterator that visits the array's keys (its indexes, as numbers).考虑数组的keys方法:它返回一个数组迭代器,它访问数组的键(它的索引,作为数字)。 Note that it returns an iterator .请注意,它返回一个迭代器 But a common use of it is:但它的一个常见用途是:

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

for-of takes an iterable , not an iterator , so why does that work? for-of需要一个iterable ,而不是一个iterator ,那为什么会这样呢?

It works because the iterator is also iterable;它之所以有效,是因为迭代器也是可迭代的; Symbol.iterator just returns this . Symbol.iterator只返回this

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:下面是我的书的第6章一个例子,我使用的:如果你想遍历所有条目,但跳过第一个,你不想使用slice到切片关子,你可以得到迭代器,读出的第一个值,然后切换到for-of循环:

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

The iterator returned by range there is not an iterable, so it fails when we try to use it with for-of . range there 返回的迭代器不是可迭代的,因此当我们尝试将它与for-of一起使用时它会失败。

To make it iterable, we'd need to either:为了使其可迭代,我们需要:

  1. Add the Symbol.iterator method at the beginning of the answer above to it, or在上面答案的开头添加Symbol.iterator方法,或者
  2. Make it inherit from %IteratorPrototype%, which already has that method让它从 %IteratorPrototype% 继承,它已经有那个方法

Sadly, TC39 decided not to provide a direct way to get the %IteratorPrototype% object.遗憾的是,TC39 决定不提供直接获取 %IteratorPrototype% 对象的方法。 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.有一种间接的方法(从数组中获取迭代器,然后获取它的原型,它被定义为 %IteratorPrototype%),但这很痛苦。

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.字符串、映射和集合也是如此。

I found that there are some more precise definitions of the terms, and these are the more definitive answers:我发现这些术语有一些更精确的定义,这些是更明确的答案:

According to the ES6 Specs and MDN :根据ES6 规范MDN

When we have当我们有

function* foo() {   // note the "*"
    yield 1;
    yield 3;
    yield 5;
}

foo is called generator function . foo被称为生成器函数 And then when we have然后当我们有

let bar = foo();

bar is a generator object . bar是一个生成器对象 And a generator object conforms to both the iterable protocol and the iterator protocol .并且生成器对象同时符合可迭代协议和迭代器协议

The simpler version is the iterator interface, which is just a .next() method.更简单的版本是迭代器接口,它只是一个.next()方法。

The iterable protocol is: for the object obj , obj[Symbol.iterator] gives a "zero arguments function that returns an object, conforming to the iterator protocol".可迭代协议是:对于对象objobj[Symbol.iterator]给出一个“返回对象的零参数函数,符合迭代器协议”。

By the title of the MDN link , it also seems we can also just call a generator object a "generator".根据MDN 链接标题,我们似乎也可以将生成器对象称为“生成器”。

Note that in Nicolas Zakas's book Understanding ECMAScript 6 , he probably loosely called a "generator function" as a "generator", and a "generator object" as an "iterator".请注意,在Nicolas Zakas 的《Understanding ECMAScript 6》一书中,他可能将“生成器函数”松散地称为“生成器”,将“生成器对象”称为“迭代器”。 The take away point is, they are really both "generator" related -- one is a generator function, and one is a generator object, or generator.要点是,它们实际上都与“生成器”相关——一个是生成器函数,一个是生成器对象或生成器。 The generator object conforms to both the iteratable protocol and the iterator protocol.生成器对象同时符合可迭代协议和迭代器协议。

If it is just an object conforming to the iterator protocol, you cannot use [...iter] or for (a of iter) .如果它只是一个符合迭代器协议的对象,则不能使用[...iter]for (a of iter) It has to be an object that conforms to the iterable protocol.它必须是一个符合可迭代协议的对象。

And then, there is also a new Iterator class, in a future JavaScript specs that is still in a draft .然后,还有一个新的 Iterator 类,在未来的 JavaScript 规范中仍处于草案中 It has a larger interface, including methods such as forEach , map , reduce of the current Array interface, and new ones, such as and take , and drop .它有一个更大的接口,包括当前Array接口的forEachmapreduce等方法,以及新的和takedrop The current iterator refers to the object with just the next interface.当前迭代器引用仅具有next接口的对象。

To answer the original question: what is the difference between an iterator and an iterable, the answer is: an iterator is an object with the interface .next() , and an iterable is an object obj such that obj[Symbol.iterator] can give a zero-argument function that, when invoked, returns an iterator.要回答最初的问题:迭代器和可迭代器有什么区别,答案是:迭代器是具有接口.next()的对象,可迭代器是对象obj使得obj[Symbol.iterator]可以给出一个零参数函数,该函数在调用时返回一个迭代器。

And a generator is both an iterable and iterator, to add to that.并且生成器既是可迭代的又是迭代器,要添加到其中。

Here's the difference, in the simplest terms:用最简单的术语来说,区别如下:

  • iterator - any object that has next function to return the next value iterator - 任何具有next函数来返回下一个值的对象
  • iterable - any object that has [Symbol.iterator] function that returns an iterator iterable - 任何具有返回iterator [Symbol.iterator]函数的对象

But when you have an object that does both, it is called IterableIterator .但是当您有一个对象同时执行这两项操作时,它就被称为IterableIterator For example, any generator function returns one.例如,任何生成器函数都返回一个。

And typical implementation for an IterableIterator goes like this: IterableIterator典型实现是这样的:

{
    [Symbol.iterator]() {
        return this; // returning iterator
    },
    next() {
        // return next value here
    }
} //=> IterableIterator

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 根据ES6,JavaScript数组迭代器本身是可迭代的吗? - Is a JavaScript array iterator itself iterable according to ES6? 在 JavaScript ES6 中,iterable[Symbol.iterator] 被绑定到 iterable 的`this` 被调用,但是 Mozilla 和 ES6 规范都没有提到它? - In JavaScript ES6, iterable[Symbol.iterator] is called with `this` bound to iterable, but Mozilla and ES6 specs both didn't mention it? 这些 ES6 导入语句之间有什么区别? - What is the difference between these ES6 import statements? Javascript (ES6) 可迭代流 - Javascript (ES6) iterable stream Javascript ES6中对象解构和普通对象分配有什么区别? - What's the difference between object destructuring and normal object assignment in Javascript ES6? 新的javascript ES6模块术语中的合格和不合格导入有什么区别? - What is the difference between qualified and unqualified imports in the new javascript ES6 module lingo? 在 JavaScript ES6 中,每次调用 obj[Symbol.iterator] function 时,可迭代对象是否会吐出一个新的迭代器? - In JavaScript ES6, does an iterable spit out a new iterator each time the obj[Symbol.iterator] function is invoked? JS ES6中()=&gt;和_ =&gt;以及(_)=&gt;之间的差异 - Difference between ()=> and _=> and (_)=> in JS ES6 ES6 Set 和 WeakSet 有什么区别? - What's the difference between ES6 Set and WeakSet? &#39;let&#39; 和 &#39;const&#39; ECMAScript 2015 (ES6) 有什么区别? - What is the difference between 'let' and 'const' ECMAScript 2015 (ES6)?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM