whileSynchrone Iteration ist ein Protokoll (Schnittstellen plus Regeln für deren Verwendung), das zwei Gruppen von Entitäten in JavaScript verbindet.
Datenquellen: Einerseits kommen Daten in allen Formen und Größen. In der Standardbibliothek von JavaScript gibt es die lineare Datenstruktur Array, die geordnete Sammlung Set (Elemente sind nach ihrer Hinzufügungszeit geordnet), das geordnete Wörterbuch Map (Einträge sind nach ihrer Hinzufügungszeit geordnet) und mehr. In Bibliotheken finden Sie baumförmige Datenstrukturen und mehr.
Datenkonsumenten: Andererseits gibt es eine ganze Klasse von Konstrukten und Algorithmen, die nur sequentiell auf ihre Eingaben zugreifen müssen: ein Wert nach dem anderen, bis alle Werte besucht wurden. Beispiele hierfür sind die for-of-Schleife und das Sprengen in Funktionsaufrufe (über ...).
Das Iterationsprotokoll verbindet diese beiden Gruppen über die Schnittstelle Iterable: Datenquellen liefern ihre Inhalte sequentiell „durch sie“; Datenkonsumenten erhalten ihre Eingaben über sie.
Abb. 18 veranschaulicht, wie Iteration funktioniert: Datenkonsumenten verwenden die Schnittstelle Iterable; Datenquellen implementieren sie.
Die JavaScript-Art, Schnittstellen zu implementieren
In JavaScript implementiert ein Objekt eine Schnittstelle, wenn es über alle Methoden verfügt, die diese beschreibt. Die in diesem Kapitel genannten Schnittstellen existieren nur in der ECMAScript-Spezifikation.
Sowohl Datenquellen als auch -konsumenten profitieren von dieser Regelung.
Wenn Sie eine neue Datenstruktur entwickeln, müssen Sie nur Iterable implementieren, und eine Fülle von Werkzeugen kann sofort darauf angewendet werden.
Wenn Sie Code schreiben, der Iteration verwendet, funktioniert dieser automatisch mit vielen Datenquellen.
Zwei Rollen (beschrieben durch Schnittstellen) bilden den Kern der Iteration (Abb. 19).
Dies sind Typdefinitionen (in der Notation von TypeScript) für die Schnittstellen des Iterationsprotokolls.
interface Iterable<T> {
[Symbol.iterator]() : Iterator<T>;
}
interface Iterator<T> {
next() : IteratorResult<T>;
}
interface IteratorResult<T> {
value: T;
done: boolean;
}Die Schnittstellen werden wie folgt verwendet:
Iterable über die Methode mit dem Schlüssel Symbol.iterator nach einem Iterator.Iterator gibt die iterierten Werte über seine Methode .next() zurück..value ist der iterierte Wert..done gibt an, ob das Ende der Iteration erreicht wurde. Sie ist nach dem letzten iterierten Wert true und davor false.Dies ist ein Beispiel für die Verwendung des Iterationsprotokolls.
const iterable = ['a', 'b'];
// The iterable is a factory for iterators:
const iterator = iterable[Symbol.iterator]();
// Call .next() until .done is true:
assert.deepEqual(
iterator.next(), { value: 'a', done: false });
assert.deepEqual(
iterator.next(), { value: 'b', done: false });
assert.deepEqual(
iterator.next(), { value: undefined, done: true });whileDer folgende Code zeigt, wie eine while-Schleife verwendet wird, um über ein Iterable zu iterieren.
function logAll(iterable) {
const iterator = iterable[Symbol.iterator]();
while (true) {
const {value, done} = iterator.next();
if (done) break;
console.log(value);
}
}
logAll(['a', 'b']);
// Output:
// 'a'
// 'b' Übung: Synchrone Iteration manuell verwenden
exercises/sync-iteration-use/sync_iteration_manually_exrc.mjs
Wir haben gesehen, wie das Iterationsprotokoll manuell verwendet wird, und es ist relativ umständlich. Aber das Protokoll ist nicht dafür gedacht, direkt verwendet zu werden – es ist dafür gedacht, über höherstufige Sprachkonstrukte verwendet zu werden, die darauf aufbauen. Dieser Abschnitt zeigt, wie das aussieht.
JavaScript-Arrays sind iterierbar. Das ermöglicht uns die Verwendung der for-of-Schleife.
const myArray = ['a', 'b', 'c'];
for (const x of myArray) {
console.log(x);
}
// Output:
// 'a'
// 'b'
// 'c'Die Destrukturierung über Array-Muster (später erklärt) verwendet ebenfalls Iteration im Hintergrund.
const [first, second] = myArray;
assert.equal(first, 'a');
assert.equal(second, 'b');Die Set-Datenstruktur von JavaScript ist iterierbar. Das bedeutet, dass for-of funktioniert.
const mySet = new Set().add('a').add('b').add('c');
for (const x of mySet) {
console.log(x);
}
// Output:
// 'a'
// 'b'
// 'c'Ebenso wie die Array-Destrukturierung.
const [first, second] = mySet;
assert.equal(first, 'a');
assert.equal(second, 'b');Die folgenden integrierten Datenquellen sind iterierbar:
Um über die Eigenschaften von Objekten zu iterieren, benötigen Sie Hilfsmittel wie Object.keys() und Object.entries(). Dies ist notwendig, da Eigenschaften auf einer anderen Ebene existieren, die unabhängig von der Ebene der Datenstrukturen ist.
Dieser Abschnitt listet Konstrukte auf, die synchrone Iteration verwenden.
Destrukturierung über ein Array-Muster
const [x,y] = iterable;Spreading (über ...) in Funktionsaufrufe und Array-Literale
func(...iterable);
const arr = [...iterable];Die for-of-Schleife
for (const x of iterable) { /*···*/ }yield*:
function* generatorFunction() {
yield* iterable;
}const obj = Object.fromEntries(iterableOverKeyValuePairs);const arr = Array.from(iterable);const m = new Map(iterableOverKeyValuePairs);
const wm = new WeakMap(iterableOverKeyValuePairs);const s = new Set(iterableOverElements);
const ws = new WeakSet(iterableOverElements);Promise-Kombinatorfunktionen: Promise.all() usw.
const promise1 = Promise.all(iterableOverPromises);
const promise2 = Promise.race(iterableOverPromises);
const promise3 = Promise.any(iterableOverPromises);
const promise4 = Promise.allSettled(iterableOverPromises); Quiz
Siehe Quiz-App.