Interatory i pętla for...of
Pętla for-of
for
Iterowanie po tablicach klasycznym sposobem.
Przykład 8.1
let myArray = ['Alfa', 'Bravo', 'Charlie', 'Delta'];
for (let i = 0; i < myArray.length; i++) {
console.log(i, myArray[i]);
}
// -> 1 • 'Alfa'
// -> 2 • 'Bravo'
// -> 3 • 'Charlie'
// -> 4 • 'Delta'
for-in
Można także wykorzystać petlę for-in
ale nie jest to zalecany sposób.
Przykład 8.2
for (let i in myArray) {
console.log(i, myArray[i]);
}
// -> 1 • 'Alfa'
// -> 2 • 'Bravo'
// -> 3 • 'Charlie'
// -> 4 • 'Delta'
Index i
będzie typu string. Kolejność iteracji nie jest gwarantowana. for..in
stworzono do iteracji po właściwościach obiektu.
forEach()
W ES5 możemy użyć metody Array.forEach()
do iterowania po tablicach.
Przykład 8.3
myArray.forEach((element, index) => {
console.log(index, element);
});
// -> 1 • 'Alfa'
// -> 2 • 'Bravo'
// -> 3 • 'Charlie'
// -> 4 • 'Delta'
Pewnym problemem jest przerwanie takiej pętli. Nie ma zastosowania break
. Ponadto:
There is no way to stop or break a
forEach()
loop other than by throwing an exception. If you need such behavior, theforEach()
method is the wrong tool. Use a plain loop instead. If you are testing the array elements for a predicate and need a Boolean return value, you can useevery()
orsome()
instead. If available, the new methodsfind()
orfindIndex()
can be used for early termination upon true predicates as well.
for-of
Pętla for-of
została stworzona do rozwiązania problemów pętli przedstawionych powyżej. Pozwala iterować po wielu typach danych (Array
, String
, TypedArray
, Map
, Set
, DOMCollection
, Iterators
, Generators
), nie powielając problemów wyżej przedstawionych rozwiązań.
Przykład 8.4
let myArray = ['Alfa', 'Bravo', 'Charlie', 'Delta'];
for (let item of myArray) {
console.log(item);
}
// -> 'Alfa'
// -> 'Bravo'
// -> 'Charlie'
// -> 'Delta'
- powyższe rozwiązanie stanowi najzwięźlejszą i najprostszą pętlę operującą na elementach tablic,
- pozbawiona jest pułapek związanych z pętlą
for-in
, - w przeciwieństwie do metody
forEach()
, dobrze współpracuje z instrukcjamibreak
,continue
,return
.
Iteratory
Tak jak instrukcje
for
iforeach
w tych innych językach, pętlafor–of
działa wyłącznie na zasadzie wywołania metody. Tym, co łączy obiektyArray
,Map
,Set
i inne, o których zdążyliśmy nadmienić, to metoda iteracyjna.Metodę iteracyjną może ponadto mieć jeszcze jeden rodzaj obiektu: dowolnie wybrany obiekt.
Podobnie jak dodanie
mójObiekt.toString()
do jakiegokolwiek obiektu sprawia, że JS od razu wie jak konwertować dany obiekt na łańcuch znaków, dodanie metodymójObiekt[Symbol.iterator]()
do dowolnego obiektu sprawi, że JS bez problemu wykona na nim pętlę.
Obiekty z metodą [Symbol.iterator]()
nazywamy obiektami iterowalnymi. Stwórzmy najprostszy możliwy iterator (obiekt, który zawiera jedynie metodę iteratora).
Przykład 8.5
let timestampForeverIterator = {
[Symbol.iterator]: () => ({
next: () => ({
done: false,
value: Date.now()
})
})
}
for (let value of timestampForeverIterator) {
console.log(value);
}
Funkcja iteratora musi zwracać obiekt zawierający metodę next()
. Metoda next()
zaś musi zwracać obiekt informujący czy iteracja dobiegła już końca (pole done
) oraz, że kolejną wartością value
jest timestamp (Date.now()
).
Funkcjonowanie takiego iteratora, mającego własności
done
ivalue
, różni się od iteratorów w innych językach. W Javie iteratory mają osobne metodyhasNext()
inext()
. W Pythonie natomiast istnieje pojedyncza metodanext()
, która zgłasza wyjątekStopIteration
po wyczerpaniu się wszystkich wartości. We wszystkich trzech przypadkach zwracane są jednak właściwie te same informacje.
Obiekt iteratora może również implementować opcjonalne metody return
i throw(wyjątek)
. Pętla for–of
wywoła metodę return
, jeśli do zakończenia pętli doszło przedwcześnie w wyniku wyjątku lub instrukcji break
bądź return
. Iterator może implementować metodę return
, jeśli potrzebne jest zwolnienie wykorzystywanych wcześniej zasobów.
Potencjalne zastosowania iteratorów:
- genrowanie(!) danych,
- ukrywanie implementacji,
- iterowanie po wybranych właściwościach obiektu.
Przykład 8.6
const randomRange = (items = 1, from = 0, to = 10) => ({
[Symbol.iterator]: () => {
let currentItemCounter = 0;
return {
next: () => {
let done = currentItemCounter++ === items;
let value = from + Math.floor(Math.random() * to) + 1;
return {
done: done,
value: done ? undefined : value
}
}
}
}
});
for (let n of randomRange(10)) {
console.log(n);
}
Przykład 8.7
class Words {
constructor(str) {
this._str = str;
this[Symbol.iterator] = () => {
const re = /\S+/g,
str = this._str;
return {
next: () => {
const match = re.exec(str);
return {
value: match ? match[0] : undefined,
done: !match
}
}
}
}
}
}
const lipsum = new Words(`Lorem ipsum dolor sit amet,
consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.`);
for (let word of lipsum) {
console.log(word);
}
// -> Lorem
// -> ipsum
// -> dolor
// -> sit
// -> amet,
// ...
Ćwiczenie
(8.1) Zaimplementować klasę OverNameIterate
przyjmującą w konstruktorze kolekcję obiektów. Klasa musi implementować iterator, pozwalający na wypisywanie z kolejnych obiektów własności name
.
let sampleCollection = [
{"name": "Calvin", "surname": "Castillo", "email": "[email protected]", "city": "Landeck"},
{"name": "Oren", "surname": "Austin", "email": "[email protected]", "city": "Morrovalle"},
{"name": "Buckminster", "surname": "Johnston", "email": "[email protected]", "city": "Olen"},
{"name": "Byron", "surname": "Cooke", "email": "[email protected]", "city": "Habergy"},
{"name": "Ciaran", "surname": "Alford", "email": "[email protected]", "city": "Bussolengo"},
{"name": "Cedric", "surname": "Battle", "email": "[email protected]", "city": "Kelkheim"},
{"name": "Alec", "surname": "Rasmussen", "email": "[email protected]", "city": "Colleretto Castelnuovo"},
{"name": "Fuller", "surname": "Hooper", "email": "[email protected]", "city": "Izel"},
{"name": "Brett", "surname": "Fox", "email": "[email protected]", "city": "Castello Tesino"},
{"name": "Armand", "surname": "Nicholson", "email": "[email protected]", "city": "Bournemouth"},
{"name": "Kennan", "surname": "Blair", "email": "[email protected]", "city": "Paranaguá"},
{"name": "Jack", "surname": "Lowe", "email": "[email protected]", "city": "Roveredo in Piano"}
];
Źródła