Czy iterowalność jest taka sama jak iterator, czy też są różne?
Wydaje się, że ze specyfikacji iterowalny jest obiekt, powiedzmy, obj
taki, który obj[Symbol.iterator]
odnosi się do funkcji, więc po wywołaniu zwraca obiekt, który ma next
metodę, która może zwrócić {value: ___, done: ___}
obiekt:
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)
Tak więc w powyższym kodzie bar
jest iterowalny i wah
jest iteratorem, a także next()
interfejsem iteratora.
Tak więc iterowalność i iterator to różne rzeczy.
Teraz jednak we wspólnym przykładzie generatora i iteratora:
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
W powyższym przypadku gen1
jest to generator i iter1
iterator, iter1.next()
który wykona właściwą pracę. Ale iter1[Symbol.iterator]
daje funkcję, która po wywołaniu zwraca iter1
, która jest iteratorem. Czy iter1
w tym przypadku jest zarówno iterowalny, jak i iterator?
Poza tym iter1
różni się od powyższego przykładu 1, ponieważ iterowalność w przykładzie 1 może dawać [1, 3, 5]
tyle razy [...bar]
, ile iter1
potrzeba , podczas gdy jest iterowalna, ale ponieważ zwraca się, która za każdym razem jest tym samym iteratorem, da tylko [1, 3, 5]
raz.
Możemy więc powiedzieć, w przypadku iteracji bar
, ile razy można [...bar]
dać wynik [1, 3, 5]
- i odpowiedź brzmi: to zależy. Czy iterowalność jest taka sama jak iterator? Odpowiedź brzmi: są to różne rzeczy, ale mogą być takie same, gdy iterowalny używa siebie jako iteratora. Czy to jest poprawne?
źródło
iter1
w tym przypadku jest zarówno iterowalny, jak i iterator? ” - tak. Wszystkie natywne iteratory są również iterowalne, zwracając się, dzięki czemu można łatwo przekazać je do konstrukcji oczekujących iteracji.Odpowiedzi:
Tak, iterables i iteratory są różne rzeczy, ale większość iteratory (w tym wszystkie te, które można uzyskać z samego JavaScript, taki jak z
keys
lubvalues
metod naArray.prototype
lub generatorów z funkcji generatora) dziedziczą z % IteratorPrototype% obiektu , który maSymbol.iterator
metody takie jak to:W rezultacie wszystkie standardowe iteratory są iterowalne. Dzięki temu możesz używać ich bezpośrednio lub używać ich w
for-of
pętlach i tym podobnych (które oczekują iteracji, a nie iteratorów).Rozważ
keys
metodę tablic: zwraca iterator tablicy, który odwiedza klucze tablicy (jej indeksy, jako liczby). Zauważ, że zwraca iterator . Ale powszechnym zastosowaniem jest:for-of
bierze iterable , a nie iterator , więc dlaczego to działa?Działa, ponieważ iterator jest również iterowalny;
Symbol.iterator
właśnie wracathis
.Oto przykład, którego używam w rozdziale 6 mojej książki: Jeśli chcesz zapętlić wszystkie wpisy, ale pomiń pierwszy, a nie chcesz
slice
wycinać podzbioru, możesz pobrać iterator, odczytać pierwszą wartość, następnie przekaż dofor-of
pętli:Zauważ, że są to wszystkie standardowe iteratory. Czasami ludzie pokazują przykłady ręcznie kodowanych iteratorów, takich jak to:
Pokaż fragment kodu
Iterator zwrócony przez
range
nie nie iterable, więc to się nie powiedzie, gdy staramy się wykorzystywać gofor-of
.Aby umożliwić iterację, musimy:
Symbol.iterator
metodę na początku powyższej odpowiedzi lubNiestety TC39 postanowił nie zapewniać bezpośredniego sposobu uzyskania obiektu% IteratorPrototype%. Istnieje sposób pośredni (pobranie iteratora z tablicy, a następnie pobranie jego prototypu, który jest zdefiniowany jako% IteratorPrototype%), ale jest to ból.
Ale i tak nie ma potrzeby ręcznego pisania iteratorów; wystarczy użyć funkcji generatora, ponieważ generator, który zwraca, jest iterowalny:
Pokaż fragment kodu
W przeciwieństwie do tego, nie wszystkie iteratory są iteratorami. Tablice są iterowalne, ale nie iteratory. Podobnie jak łańcuchy, mapy i zestawy.
źródło
Odkryłem, że istnieją bardziej precyzyjne definicje terminów i są to bardziej ostateczne odpowiedzi:
Zgodnie ze specyfikacją ES6 i MDN :
Kiedy mamy
foo
nazywa się funkcją generatora . A potem, kiedy mamybar
jest generatorem obiektu . A obiekt generatora jest zgodny zarówno z protokołem iteracyjnym, jak i protokołem iteratora .Prostszą wersją jest interfejs iteratora, który jest tylko
.next()
metodą.Iterable protokół jest: dla obiektu
obj
,obj[Symbol.iterator]
daje „zero argumentów funkcja, która zwraca obiekt, zgodny z protokołem iteracyjnej”.Według tytułu łącza MDN wydaje się, że możemy również nazwać obiekt generatora „generatorem”.
Zauważ, że w książce Nicolasa Zakasa Zrozumienie ECMAScript 6 prawdopodobnie luźno nazwał „funkcję generatora” jako „generator”, a „obiekt generatora” jako „iterator”. Punktem wyjścia jest to, że tak naprawdę oba są związane z „generatorem” - jeden jest funkcją generatora, a drugi jest obiektem generatora lub generatorem. Obiekt generatora jest zgodny zarówno z protokołem iterowalnym, jak i protokołem iteratora.
Jeśli jest to tylko obiekt zgodny z protokołem iteratora , nie można użyć
[...iter]
lubfor (a of iter)
. Musi to być obiekt zgodny z iterowalnym protokołem.I jest też nowa klasa Iterator, w przyszłych specyfikacjach JavaScript, która wciąż jest w wersji roboczej . Ma większy interfejs, w tym metod, takich jak
forEach
,map
,reduce
aktualny interfejs tablicy i nowych, jak itake
, idrop
. Bieżący iterator odnosi się do obiektu tylko znext
interfejsem.Aby odpowiedzieć na pierwotne pytanie: jaka jest różnica między iteratorem a iterowalnym, odpowiedź brzmi: iterator to obiekt z interfejsem
.next()
, a iterowalny to obiektobj
taki, któryobj[Symbol.iterator]
może dać funkcję zerowego argumentu, która po wywołaniu zwraca iterator.Generator jest zarówno iterowalny, jak i iteracyjny, aby do tego dodać.
źródło