問題描述
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?
推薦答案
是的,iterables 和 iterators 是不同的東西,但是大多數迭代器(包括你所有的迭代器)從 JavaScript 本身獲取,例如從 Array.prototype
上的 keys
或 values
方法或生成器函數的生成器)繼承自 %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:
- 在上面答案的開頭添加
Symbol.iterator
方法,或者 - 讓它繼承自 %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 中,可迭代對象和迭代器有什么區別?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!