[英]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
上的keys
或values
方法或来自生成器函数的生成器)继承自%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:为了使其可迭代,我们需要:
Symbol.iterator
method at the beginning of the answer above to it, or在上面答案的开头添加Symbol.iterator
方法,或者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".可迭代协议是:对于对象obj
, obj[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接口的forEach
、 map
、 reduce
等方法,以及新的和take
、 drop
。 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.