Uczymy się, czym są iteratory w JavaScript i jak z nich korzystać. Do dzieła!
Na początek funkcja, która sprawdza, czy obiekt ma iterator:
function hasIterator(obj) {
return obj && typeof obj[Symbol.iterator] === "function";
}
Teraz bardzo prosta klasa:
class Collection {
constructor(arr) {
this.arr = arr;
}
}
let items = new Collection([1,2,3,4,5]);
console.log(hasIterator(items));
//false
Jak widać nie posiada iteratora. Napiszmy go:
class Collection {
constructor(arr) {
this.arr = arr;
}
[Symbol.iterator]() {
let items = this.arr;
let index = 0;
return {
next: function() {
return {
done: (index === items.length) ? true : false,
value: items[index++]
};
}
};
}
}
Logika jest prosta – funkcja next zwraca done i value. Indeksy danego iteratora są pamiętane za pomocą closure.
Możemy to przetestować:
let items = new Collection([1,2,3,4,5]);
console.log(hasIterator(items));
//true
for(let el of items) {
console.log(el);
}
// 1
// 2
// 3
// 4
// 5
Iterator możemy napisać inaczej, choć pewnie nie zrozumiemy jak to działa, jeżeli nie wiemy, czym są generatory:
function hasIterator(obj) {
return obj && typeof obj[Symbol.iterator] === "function";
}
class Collection {
constructor(arr) {
this.arr = arr;
}
*[Symbol.iterator]() {
yield *this.arr;
}
}
let items = new Collection([1,2,3,4,5]);
console.log(hasIterator(items));
//true
for(let el of items) {
console.log(el);
}
// 1
// 2
// 3
// 4
// 5
„Jak” jest tutaj mało ważne, bo jest to świetny patent na tworzenie iteratorów na zasadzie kopiuj-wklej, ale o generatorach niebawem też sobie powiemy.