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
foriforeachw tych innych językach, pętlafor–ofdziała wyłącznie na zasadzie wywołania metody. Tym, co łączy obiektyArray,Map,Seti 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
doneivalue, 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ątekStopIterationpo 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