Iteratory (Symbol. iterator)
Protokół iteracji: obiekt z metodą [Symbol.iterator]() działa z for...of, spreadem i destrukturyzacją.
Przykład
#const tasks = ['Quiz', 'Projekt']
const iterator = tasks[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())Tak właśnie działa pętla for...of: pobiera iterator i wywołuje next(), dopóki nie dostanie done: true.
Definicja i zastosowanie
#Iteracja w JavaScripcie opiera się na prostej umowie. Obiekt iterowalny ma metodę o kluczu Symbol.iterator, która zwraca iterator. Iterator to obiekt z metodą next(), zwracającą przy każdym wywołaniu wynik { value, done }: kolejną wartość i informację, czy to już koniec.
Z tej umowy korzystają for...of, spread [...x], destrukturyzacja tablicowa, Array.from(), konstruktory Map i Set oraz Promise.all(). Tablice, napisy, Map, Set i listy NodeList są iterowalne od razu. Zwykły obiekt {} nie jest, dlatego for...of na nim rzuca TypeError.
Własny obiekt lub klasę uczynisz iterowalnymi, dodając metodę [Symbol.iterator](). Najprościej zapisać ją jako generator, *[Symbol.iterator]() {}, bo generatory same tworzą poprawne iteratory. Przeglądarki wydane od 2025 roku mają też metody pomocnicze iteratorów, jak map(), filter(), take() i toArray(), które przetwarzają wartości leniwie, bez tworzenia tablic pośrednich.
Składnia
#const iterator = iterable[Symbol.iterator]()
iterator.next()
class Name {
*[Symbol.iterator]() {
yield value
}
}Więcej przykładów
#class Course {
constructor(title, lessons) {
this.title = title
this.lessons = lessons
}
*[Symbol.iterator]() {
for (const lesson of this.lessons) {
yield `${this.title}: ${lesson}`
}
}
}
const course = new Course('CSS', ['Selektory', 'Flexbox', 'Grid'])
for (const lesson of course) {
console.log(lesson)
}
console.log([...course].length)const countdown = {
from: 3,
[Symbol.iterator]() {
let current = this.from
return {
next() {
if (current > 0) {
return { value: current--, done: false }
}
return { value: undefined, done: true }
}
}
}
}
console.log([...countdown])
const [first, second] = countdown
console.log(first, second)Ten sam obiekt działa ze spreadem i z destrukturyzacją, bo obie konstrukcje wywołują [Symbol.iterator]() i pobierają wartości przez next().
const scores = { Ania: 120, Kuba: 95 }
try {
for (const score of scores) {
console.log(score)
}
} catch (error) {
console.log(error.name)
}
console.log(typeof scores[Symbol.iterator])
console.log(typeof [][Symbol.iterator])
for (const [name, points] of Object.entries(scores)) {
console.log(name, points)
}Obsługa przeglądarek
#Szeroko dostępne
Działa we wszystkich nowoczesnych przeglądarkach, także na telefonach. Możesz używać bez obaw.
- Chrome
- Edge
- Firefox
- Safari
Dobre praktyki
#- Metodę
[Symbol.iterator]()najłatwiej napisać jako generator zyield. Nie musisz wtedy ręcznie budować obiektów{ value, done }. - Obiekt iterowalny, np. tablica, tworzy nowy iterator przy każdej pętli, więc można po nim przechodzić wielokrotnie. Sam iterator albo generator po wyczerpaniu nie zwróci już żadnej wartości.
- Czy wartość da się przejść pętlą
for...of, sprawdzisz warunkiemtypeof value?.[Symbol.iterator] === "function".
Powiązane hasła
#- function*Deklaruje generator: funkcję, która oddaje kolejne wartości przez yield i wstrzymuje się między nimi.
- for...ofPrzechodzi po kolejnych wartościach obiektu iterowalnego: tablicy, napisu, Map, Set i innych.
- Spread (...)Składnia spread rozkłada tablicę, napis lub obiekt na pojedyncze elementy: do kopiowania, łączenia i przekazywania argumentów.
- SymbolTyp prymitywny do tworzenia unikalnych identyfikatorów, np. kluczy właściwości, które nie kolidują z innymi.
- Array.from()Tworzy tablicę z obiektu iterowalnego lub „tablicopodobnego”.
- DestrukturyzacjaDestrukturyzacja (destructuring) wyciąga wartości z tablic i obiektów prosto do osobnych zmiennych.
Widzisz błąd albo brakuje przykładu? Napisz do nas.