Ablaufsteuerung

Der Kontrollfluss ist die Reihenfolge, in der der JavaScript-Interpreter ausgeführt wird. Aussagen. Wenn ein Skript keine Anweisungen enthält, die seinen Ablauf ändern, ist es Zeile für Zeile von Anfang bis Ende ausgeführt. Kontrollstrukturen sind wird bestimmt, ob eine Reihe von Anweisungen auf der Grundlage eines eine Reihe von Kriterien festgelegt, eine Reihe von Anweisungen wiederholt ausgeführt oder eine Reihenfolge von Anweisungen.

Bedingte Anweisungen bestimmen, ob Code basierend auf einem oder weitere Bedingungen. Eine bedingte Anweisung führt den darin enthaltenen Code aus, wenn die zugehörige Bedingung (oder Gruppe von Bedingungen) wird als true ausgewertet. Andernfalls wird der wird der Code übersprungen.

if...else

Eine if-Anweisung wertet eine Bedingung innerhalb der übereinstimmenden Klammern aus, die folgen. Wenn die Bedingung in den Klammern true ergibt, oder block-Anweisung nach den übereinstimmenden Klammern ausgeführt:

if ( true ) console.log( "True." );
> "True."

if ( true ) {
   
const myString = "True.";
    console
.log( myString );
}
> "True."

Wenn die Bedingung in den Klammern false ergibt, ist die Anweisung, wird sie ignoriert:

if ( false ) console.log( "True." );

Ein else-Keyword, das direkt auf eine if-Anweisung folgt, und seine bedingt ausgeführte Anweisung gibt die Anweisung an, die ausgeführt werden soll, wenn die Die Bedingung if wird als false ausgewertet:

if ( false ) console.log( "True." )''
else console.log( "False" );
> "False."

Um mehrere if-Anweisungen miteinander zu verketten, können Sie bedingt ausgeführte Anweisung nach else einer weiteren if-Anweisung:

const myCondition = 2;
if ( myCondition === 5 ) console.log( "Five." );
else if ( myCondition === 2 ) console.log( "Two." );

Wir empfehlen dringend, die Syntax von Blockanweisungen nach Bedingungen zu verwenden, verbessert die Lesbarkeit, aber else if-Klauseln sind oft eine Ausnahme davon:

if ( myCondition === 5 ) {
    console
.log( "Five." );
} else if ( myCondition === 3 ) {
    console
.log( "Three" );
} else {
    console
.log( "Neither five nor three." );
}
> "Neither five nor three."

Ternärer Operator

Mit if wird eine Anweisung bedingt ausgeführt. Der ternäre Operator (genauer aber seltener als ternärer Bedingungsoperator bezeichnet) ist eine Kurzschreibweise. um einen Ausdruck bedingt auszuführen. Wie der Name schon sagt, ist die ternäre ist der einzige JavaScript-Operator, der drei Operanden verwendet:

  • Eine auszuwertende Bedingung, gefolgt von einem Fragezeichen (?).
  • Ausdruck, der ausgeführt werden soll, wenn die Bedingung true ergibt, gefolgt von einem Doppelpunkt (:).
  • Ausdruck, der ausgeführt werden soll, wenn die Bedingung false ergibt.

Dies wird häufig verwendet, um einen Wert bedingt festzulegen oder zu übergeben:

const myFirstResult  = true  ? "First value." : "Second value.";
const mySecondResult = false ? "First value." : "Second value.";

myFirstResult
;
> "First value."

mySecondResult
;
> "Second value."

switch...case

Verwenden Sie die Anweisung switch, um den Wert eines Ausdrucks mit einer Liste von potenzielle Werte, die mit einem oder mehreren case-Keywords definiert werden. Diese Syntax ist ungewöhnlich, weil es auf einigen der ersten Designentscheidungen von JavaScript beruht. In der Syntax switch...case wird das Keyword switch verwendet, gefolgt von einem Ausdruck, in Klammern gefolgt von einem übereinstimmenden Paar geschweifter Klammern ausgewertet. Der Text von switch kann case Keywords enthalten, in der Regel ein oder mehrere Keywords. gefolgt von einem Ausdruck oder Wert gefolgt von einem Doppelpunkt (:).

Wenn der Interpreter einen case mit einem Wert erreicht, der dem Ausdruck entspricht wird in der Klammer nach dem Keyword switch ausgewertet, , die dieser case-Klausel folgen:

switch ( 2 + 2 === 4 ) {
 
case false:
    console
.log( "False." );
 
case true:
    console
.log( "True." );
}
> "True."

Alle Anweisungen, die auf den übereinstimmenden case folgen, werden ausgeführt, selbst wenn sie in einer block-Anweisung enthalten.

switch ( 2 + 2 === 4 ) {
   
case false:
    console
.log( "False." );
 
case true:
    let myVariable
= "True.";
    console
.log( myVariable );

}
> "True."

Ein Problem bei der Verwendung von switch…case besteht darin, dass der Parameter Der JavaScript-Interpreter führt eine beliebige Anweisung aus, die auf das übereinstimmende case folgt, auch in anderen case-Klauseln. Dies wird als „Fall-Through“ bezeichnet. zu den nächsten case:

switch ( 2 + 2 === 7 ) {
   
case false:
    console
.log( "False." );
 
case true:
    console
.log( "True." );
}
> "False."
> "True."

Um ein Fehlschlagen zu vermeiden, beenden Sie jeden Fall mit dem Keyword break, das stoppt sofort die Bewertung des switch-Texts:

switch ( 2 + 2 === 7 ) {
   
case false:
    console
.log( "False." );
   
break;
 
case true:
    console
.log( "True." );
   
break;
}
> "False."

Wenn keine case mit dem bedingten Wert übereinstimmt, wählt switch die default aus wenn es eine gibt:

switch ( 20 ) {
   
case 5:
    console
.log( "The value was five." );
   
break;
 
case 10:
    console
.log( "The value was ten." );
   
break;
 
default:
    console
.log( "The value was something unexpected." );
}
> "The value was something unexpected."

Die Absturzrate gilt jedoch auch für default, was möglicherweise zu zu unerwarteten Ergebnissen führen. Um dies zu beheben, beenden Sie die default-Anweisung mit break, oder sie an letzter Stelle in der Fallliste platzieren.

switch ( 20 ) {
 
default:
    console
.log( "The value was something unexpected." );
 
case 10:
    console
.log( "The value was ten." );
   
break;
 
case 5:
    console
.log( "The value was five." );
   
break;
}
> The value was something unexpected.
> The value was ten.

Da case-Klauseln kein block-Anweisung für die Gruppierung Bei mehreren Anweisungen werden keine case- und default-Anweisungen erstellt. lexikalischen Geltungsbereich an sich:

let myVariable;
switch ( true ) {
 
case true:
    let myVariable
= "True.";
   
break;
 
default:
    let myVariable
= "False.";
   
break;
}
> Uncaught SyntaxError: redeclaration of let myVariable

Verwenden Sie Blockanweisungen, um den Umfang zu verwalten:

let myVariable;
switch ( true ) {
 
case true: {
    let myVariable
= "True.";
   
break;
 
}
 
default: {
    let myVariable
= "False.";
   
break;
 
}
}

Schleifen und Iterationen

Mit Schleifen können Sie Anweisungen wiederholen, solange eine Bedingung erfüllt ist. bis eine Bedingung erfüllt ist. Schleifen verwenden, um eine Reihe von Anweisungen in einem festen bis ein bestimmtes Ergebnis erreicht ist oder bis der das Ende einer iterierbaren Datenstruktur erreicht (zum Beispiel das letzte Element in ein Array, eine Map oder eine Gruppe, die letzte Eigenschaft eines Objekts oder das letzte Zeichen in eine Zeichenfolge).

Loops unterbrechen „Von oben nach unten“ der Ausführung eines Skripts durch Iteration über eine Reihe von Anweisungen, bis eine oder mehrere Bedingungen erfüllt sind oder nicht mehr erfüllt sind. Dies hängt von der Syntax ab, die zum Erstellen der Schleife verwendet wurde. Nach dem Ende der Schleife die Ausführung auf die folgenden Anweisungen an. Im folgenden Beispiel Die Anweisungen im Hauptteil der Schleife werden dreimal vor der geht der Dolmetscher weiter:

let iterationCount = 0;
console
.log( "Pre-loop." );
while( iterationCount < 3 ) {
  iterationCount
++;
  console
.log( "Loop iteration." );
}
console
.log( "Continuing on." );
> "Pre-loop."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Continuing on."

Wenn die Bedingungen während der Ausführung der Schleife nicht erfüllt werden können, wird die Schleife fortgesetzt. auf unbestimmte Zeit. Diese Endlosschleifen sind ein häufiges Problem bei der Programmierung, dazu führen, dass der Haupt-Ausführungsthread oder sogar einen Browsertab zum Absturz bringen.

Das folgende Beispiel wird ausgeführt, solange der boolesche Wert true beibehalten wird. true Da boolesche Werte unveränderlich sind, entsteht eine Endlosschleife.

console.log( "Pre-loop." );
while( true ) {
console
.log( "Loop iteration." );
}
> "Pre-loop."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."

Vermeiden Sie Endlosschleifen in Ihrem Produktionscode. Wenn Sie aus Versehen während der Entwicklung behoben werden, können Sie das Problem beheben, indem Sie den aktuellen Browsertab schließen Aktualisieren Sie Ihren Code, damit die Schleife nicht mehr endlos ist, und öffnen Sie die Seite.

while

Eine while-Schleife wird mit dem Schlüsselwort while gefolgt von einem Paar von Daten erstellt. übereinstimmende Klammern mit einer auszuwertenden Bedingung. Wenn die angegebene Bedingung zuerst true ergibt, wird die Anweisung (oder block-Anweisung) wird ausgeführt. Andernfalls wird die Schleife nie ausgeführt. Nach jedem wird die Bedingung neu ausgewertet. Ist sie immer noch true, wird die Schleife wiederholt wird.

let iterationCount = 0;
while( iterationCount < 3 ) {
  iterationCount
++;
  console
.log( `Loop ${ iterationCount }.` );
}
> "Loop 1."
> "Loop 2."

Wenn der Interpreter eine continue-Anweisung in einer while-Schleife findet, wird diese Anweisung gestoppt Iteration, wertet die Bedingung neu aus und setzt die Schleife wenn möglich fort:

let iterationCount = 0;
while( iterationCount <= 5 ) {
  iterationCount
++;
 
if( iterationCount === 3 ) {
   
continue;
 
}
  console
.log( `Loop ${ iterationCount }.` );
}
console
.log( "Loop ended." );
> "Loop 1."
> "Loop 2."
> "Loop 4."
> "Loop 5."
> "Loop ended."

Wenn der Interpreter eine break-Anweisung in einer while-Schleife findet, wird diese Iteration anhält und die Bedingung nicht neu ausgewertet wird, sodass der Interpreter weitermachen kann:

let iterationCount = 1;
while( iterationCount <= 5 ) {
 
if( iterationCount === 3 ) {
    console
.log(`Iteration skipped.``);`
   
break;
 
}
  console
.log( `Loop ${ iterationCount }.` );
  iterationCount
++;
}
console
.log( "Loop ended." );
> "Loop 1."
> "Loop 2."
> "Iteration skipped.
> "
Loop ended."

Sie können while verwenden, um eine bestimmte Anzahl von Durchläufen zu iterieren, wie in den Beispiel aus dem vorherigen Beispiel. Der häufigste Anwendungsfall für while ist jedoch eine Schleife Unbestimmte Länge:

let randomize = () => Math.floor( Math.random() * 10 );
let randomNum
= randomize();
while( randomNum !== 3 ){
  console
.log( `The number is not ${ randomNum }.` );
  randomNum
= randomize();
}
console
.log( `The correct number, ${ randomNum }, was found.` );
> "The number is not 0."
> "The number is not 6."
> "The number is not 1."
> "The number is not 8."
> "The correct number, 3, was found."

do...while

do...while ist eine Variante der while-Schleife, in der die Bedingung Die Bewertung erfolgt am Ende jeder Iteration der Schleife. Das bedeutet, dass der Hauptteil der Schleife wird immer mindestens einmal ausgeführt.

Um eine do...while-Schleife zu erstellen, verwenden Sie das Schlüsselwort do, gefolgt von der Anweisung (oder Blockanweisung) die bei jeder Iteration der Schleife ausgeführt wird. Fügen Sie direkt nach dieser Anweisung while und Klammern mit der Bedingung, die ausgewertet werden soll. Wann? diese Bedingung nicht mehr als true ausgewertet wird, endet die Schleife.

let iterationCount = 1;
do {
  console
.log( `Loop ${ iterationCount }.` );
  iterationCount
++;
} while ( iterationCount < 3 );
> "Loop 1."
> "Loop 2."
> "Loop 3."

Wie bei einer while-Schleife ist auch hier der häufigste Anwendungsfall für do...while eine Schleife mit Unbestimmte Länge:

let randomNum;
do {
  randomNum
= ( () => Math.floor( Math.random() * 10 ) )();
  console
.log( `Is the number ${ randomNum }?` );
} while ( randomNum !== 3 );
console
.log( `Yes, ${ randomNum } was the correct number.` );
> "Is the number 9?"
> "Is the number 2?"
> "Is the number 8?"
> "Is the number 2?"
> "Is the number 3?"
> "Yes, 3 was the correct number."

for

Verwenden Sie for-Schleifen, um über eine bekannte Anzahl zu iterieren. In Legacy-Codebasen war dies wird häufig zur Iteration über die Elemente in einem Array verwendet.

Um eine for-Schleife zu erstellen, verwenden Sie das Schlüsselwort for gefolgt von mehreren Klammern die die folgenden drei Ausdrücke akzeptiert und durch Semikolons:

  1. Ein Ausdruck, der ausgewertet wird, wenn die Schleife beginnt
  2. Eine Bedingung, die bestimmt, ob die Schleife fortgesetzt werden soll
  3. Ein Ausdruck, der am Ende jeder Schleife ausgeführt werden soll

Fügen Sie nach diesen Klammern die Anweisung (normalerweise ein blockstatement) wird in die während der Schleife ausgeführt werden.

for( let i = 0; i < 3; i++ ) {
  console
.log( "This loop will run three times.")
}

Der erste Ausdruck initialisiert eine Variable, die als Zähler fungiert. Dieses Ausdruck wird einmal vor der ersten Iteration der Schleife ausgewertet. Sie können Initialisieren Sie diese Variable mit let (oder bisher var) wie jede andere und ihr Umfang ist der Textkörper der Schleife. Diese Variablen können beliebige eine gültige Kennung, wird aber häufig als i für "Iteration" bezeichnet oder „Index“. Dies scheint den etablierten Best Practices für vorhersehbare ID-Namen aber die Konvention ist so gut etabliert, dass sie auch für andere Entwickler auf der auf einen Blick. Da indexierte Sammlungen auf null gesetzt werden, haben diese Variablen fast immer einen Anfangswert von 0.

Wie bei anderen Schleifenformen ist die Bedingung ein Ausdruck, der bestimmt, ob die Schleife ausgeführt werden soll. Dies wird meistens verwendet, um eine Obergrenze an den Iterationszähler gebunden. Der Interpreter wertet die Bedingung vor die for-Schleife zum ersten Mal ausführen.Wenn die Bedingung anfänglich nicht als true ausgewertet wird, wird der Textkörper der Schleife nicht ausgeführt.

Der letzte Ausdruck wird am Ende jeder Iteration über die Schleife ausgeführt. Sie wird normalerweise verwendet, um die ID um eins zu erhöhen.

for-Schleifen iterieren in älteren Versionen am häufigsten durch Arrays. Codebasen. In diesen Fällen ist die für die Fortsetzung der Schleife angegebene Bedingung eine Die Iterationsanzahl ist kleiner oder gleich der Länge des Arrays, das iteriert wird durch. Die zum Verfolgen der aktuellen Iterationsanzahl verwendete Variable dient Wert erhöhen, der mit diesem Index im Array verknüpft ist, sodass jedes Element Das Array, auf das reagiert werden soll:

var myArray = [ true, false, true ];
for( let i = 0; i <= myArray.length; i++ ) {
  console
.log( myArray[ i ] );
}
> true
> false
> true

Dieser Ansatz wird nicht mehr verwendet und durch modernere Ansätze zur iterierbare Datenstrukturen durchlaufen.

for [...] of [...]

Verwenden Sie for...of...-Schleifen, um über die Werte zu iterieren, die in einem iterable Datenstruktur, z. B. ein Array, ein Satz oder eine Zuordnung.

In einer for...of...-Schleife wird das Schlüsselwort for gefolgt von einem Satz Klammern verwendet. die eine Variable enthält, gefolgt von of, und dann der iterierten Datenstruktur. vorbei. Die Variable kann eine Deklaration sein, die hier mit let, const oder var, eine zuvor im aktuellen Bereich deklarierte Variable, ein Objekt oder eine Instanz von desstrukturierenden Auftrags. Sie enthält den Wert des Elements, das der aktuellen Iteration entspricht aus der Schleife.

const myIterable = [ true, false, true ];
for( const myElement of myIterable ) {
  console
.log( myElement );
}
> true
> false
> true

In diesem Beispiel funktioniert die Verwendung von const für myElement, obwohl myElement in jeder Iteration der Schleife einen neuen Wert erhalten. Das liegt daran, dass Variablen die mit let oder const deklariert sind, auf die Blockanweisung im Schleife Die Variable wird zu Beginn jeder Iteration initialisiert und bei das Ende dieser Iteration.

for...in...

Verwenden Sie for...in...-Schleifen, um über die aufzählbaren Eigenschaften eines Objekts zu iterieren. einschließlich aufzählbarer übernommener Eigenschaften. Wie bei der Schleife for...of... wird mit In der Schleife for...in... wird das Schlüsselwort for gefolgt von einem Klammernpaar verwendet die eine Variable enthält, die den Wert des Eigenschaftsschlüssels enthält, der dem mit der aktuellen Iteration der Schleife. Auf diese Variable folgt der Parameter in und dann das Objekt, über das iteriert wird:

const myObject = { "myProperty" : true, "mySecondProperty" : false };
for( const myKey in myObject ) {
  console
.log( myKey );
}
> "myProperty"
> "mySecondProperty"

Auch wenn sich der Wert von myKey mit jeder Iteration der Schleife ändert, können Sie const ohne Fehler verwenden, da die Variable effektiv verworfen wird. am Ende jeder Iteration und dann zu Beginn neu erstellt.

Der mit jedem Attributschlüssel verknüpfte Wert ist für den Nutzer nicht direkt verfügbar for...in...-Syntax. Da die Schleife Zugriff auf einen Eigenschaftsschlüssel hat, können Sie mit diesem Schlüssel „nachschlagen“, sein Wert:

const myObject = { "myProperty" : true, "mySecondProperty" : false };
for( const myKey in myObject ) {
 
const myValue = myObject[ myKey ];
  console
.log( `${ myKey } : ${ myValue }` );
}
> "myProperty : true"
> "mySecondProperty : false"

Attribute, die von integrierten Konstruktoren übernommen werden, sind nicht aufzählbar, d. h., for...in... iteriert nicht durch Properties, die von Object übernommen wurden -Konstruktor. Alle aufzählbaren Eigenschaften im Prototypenkette enthalten:

const myPrototype = { "protoProperty" : true };
const myObject = Object.create( myPrototype, {
    myProperty
: {
    value
: true,
    enumerable
: true
   
}
});
for ( const myKey in myObject ) {
 
const myValue = myObject[ myKey ];
  console
.log( `${ myKey } : ${ myValue }` );
}
> "myProperty : true"
> "protoProperty : true"

JavaScript bietet integrierte Methoden, mit denen bestimmt werden kann, ob eine Eigenschaft ein direkte Eigenschaft des Objekts statt einer Eigenschaft des Prototyps des Objekts Kette: Modern Object.hasOwn() und alte Object.prototype.hasOwnProperty()-Methoden. Diese evaluieren, ob eine bestimmte Eigenschaft vererbt (oder nicht deklariert) ist Gibt true nur für die unmittelbaren Eigenschaften eines angegebenen Objekts zurück:

const myPrototype = { "protoProperty" : true };
const myObject = Object.create( myPrototype, {
    myProperty
: {
    value
: true,
    enumerable
: true
   
}
});
for ( const myKey in myObject ) {
 
const myValue = myObject[ myKey ];
 
if ( Object.hasOwn( myObject, myKey ) ) {
    console
.log( `${ myKey } : ${ myValue }` );
 
}
}
> "myProperty : true"

Es gibt auch drei statische Methoden, die jeweils ein Array zurückgeben, das aus einem Auflistbare Schlüssel (Object.keys()), Werte (Object.values()) oder Schlüssel/Wert-Paare (Object.entries()):

const myObject = { "myProperty" : true, "mySecondProperty" : false };
Object.keys( myObject );
> Array [ "myProperty", "mySecondProperty" ]

So können Sie Objektschlüssel, Werte oder Schlüssel/Wert-Paare (mithilfe von destruktiver Zuweisung) ohne Eigenschaften, die dem Prototyp dieses Objekts gehören:

const myPrototype = { "protoProperty" : "Non-enumerable property value." };
const myObject = Object.create( myPrototype, {
    myProperty
: {
    value
: "Enumerable property value.",
    enumerable
: true
   
}
});

for ( const propKey of Object.keys( myObject ) ) {
  console
.log( propKey );
}
> "myProperty"

for ( const propValue of Object.values( myObject ) ) {
  console
.log( propValue );
}
> "Enumerable property value."

for ( const [ propKey, propValue ] of Object.entries( myObject ) ) {
  console
.log( `${ propKey } : ${ propValue }` );
}
> "myProperty : Enumerable property value."

forEach()

Die forEach()-Methoden, die vom Array bereitgestellt werden, Karte, Set NodeList-Konstruktoren sind eine nützliche Abkürzung für die Iteration über Daten, im Kontext einer Rückruffunktion. Im Gegensatz zu anderen Schleifenformen Schleife, die mit einer beliebigen forEach()-Methode erstellt wurde, kann nicht unterbrochen werden mit break oder continue

forEach ist eine Methode, die dem Prototyp jeder Datenstruktur gehört. Jeweils forEach erwartet eine Callback-Funktion als Argument, auch wenn sie in Bezug auf der Argumente enthalten, die beim Aufruf der Funktion enthalten sind. Eine zweite, optionale Argument gibt einen this-Wert an, der als Aufrufkontext für die Callback-Funktion verwendet werden.

Die mit Array.forEach verwendete Callback-Funktion stellt Parameter bereit, die den Wert des aktuellen Elements, den Index des aktuellen Elements und das Array, in dem die Methode forEach aufgerufen wurde:

const myArray = [ true, false ];
myArray
.forEach( ( myElement, i, originalArray ) => {
  console
.log( i, myElement, originalArray  );
});
> 0 true Array(3) [ true, false ]
> 1 false Array(3) [ true, false ]

Die mit Map.forEach verwendete Callback-Funktion stellt Parameter bereit, die die Wert, der dem aktuellen Element zugeordnet ist, den Schlüssel, der dem aktuellen Element zugeordnet ist und der Zuordnung, in der die forEach-Methode aufgerufen wurde:

const myMap = new Map([
 
['myKey', true],
 
['mySecondKey', false ],
]);
myMap
.forEach( ( myValue, myKey, originalMap ) => {
    console
.log( myValue, myKey, originalMap  );
});
> true "myKey" Map { myKey true, mySecondKey false }
> false "mySecondKey" Map { myKey true, mySecondKey false }

Ein Set.forEach-Callback enthält ähnliche Parameter. Da Set keine oder Schlüssel, die sich von Werten unterscheiden, stellt das zweite Argument stattdessen ein redundanten, ignorierbaren Wert, strikt auf Konsistenz der Syntax mit dem andere forEach-Methoden.

const mySet = new Set([ true, false ]);
mySet
.forEach( ( myValue, myKey, originalSet ) => {
  console
.log( myValue, myKey, originalSet  );
});
> true true Set [ true, false ]
> false false Set [ true, false ]

Iteratoren

Ein Iterable ist eine Datenstruktur, die aus einzelnen Elementen besteht, mit den zuvor beschriebenen Ansätzen iteriert. Ein Iterator ist ein iterierbares Objekt, das dem Iterator-Protokoll entspricht, das heißt, es muss next()-Methode, die nacheinander die darin enthaltenen Elemente durchläuft, jedes Mal, wenn diese Methode aufgerufen wird, und gibt ein Objekt für jede sequenzielle in einem bestimmten Format.

Die in JavaScript integrierten iterierbaren Datenstrukturen (z. B. Array Map und Set) sind keine Iterationen in und aber sie erben für alle eine iterator-Methode, auf die über die @@iterator bekanntes Symbol, gibt ein Iterator-Objekt zurück, das aus der iterierbaren Datenstruktur erstellt wurde:

const myIterable = [ 1, 2, 3 ];
const myIterator = myIterable[ Symbol.iterator ]();

myIterable
;
> (3) [1, 2, 3]

myIterator
;
> Array Iterator {}

Durch das Aufrufen der Methode next() für einen Iterator werden die darin enthaltenen Elemente schrittweise durchlaufen. enthält jeweils ein Objekt, wobei jeder Aufruf ein Objekt mit zwei Eigenschaften: value, die den Wert des aktuellen Elements enthalten, und done, ein boolescher Wert, der uns mitteilt, ob der Iterator das letzte Element in der Datenstruktur. Der Wert von done ist nur bei einem Aufruf von next() true führt zu dem Versuch, auf ein Element nach dem letzten Element im -Iterator.

const myIterable = [ 1, 2, 3 ];
const myIterator = myIterable[ Symbol.iterator ]();

myIterator
.next();
> Object { value: 1, done: false }

myIterator
.next();
> Object { value: 2, done: false }

myIterator
.next();
> Object { value: 3, done: false }

myIterator
.next();
> Object { value: undefined, done: true }

Generatorfunktionen

Verwenden Sie das Schlüsselwort function* (beachten Sie das Sternchen), um einen Generator zu deklarieren -Funktion verwenden oder einen Generatorfunktionsausdruck definieren:

function* myGeneratorFunction() { };

Wie iteratoren behalten auch Generatorfunktionen den Status bei. Durch Aufrufen einer Generatorfunktion gibt ein neues Generator-Objekt zurück, gibt jedoch nicht sofort den Code im Hauptteil der Funktion ausführen:

function* myGeneratorFunction() {
  console
.log( "Generator function body ")
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject
;
> Generator {  }

typeof myGeneratorObject;
> "object"

Generator-Objekte folgen dem Iterator-Protokoll. Der Wert, den jeder Aufruf next() für die Rückgabe einer Generatorfunktion wird durch einen yield-Ausdruck bestimmt. die die Ausführung der Generatorfunktion anhält und den Wert der Funktion Ausdruck, der das yield-Keyword enthält. Spätere Aufrufe an next() Ausführung der Funktion fortsetzen, beim nächsten yield-Ausdruck anhalten und und gibt den zugehörigen Wert zurück.

function* myGeneratorFunction() {
 
yield "My first yielded value.";
 
yield "My second yielded value.";
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject
.next();
> Object { value: "My first yielded value.", done: false }

myGeneratorObject
.next();
> Object { value: "My second yielded value.", done: false }

Wenn next() aufgerufen wird, nachdem keine weiteren Werte mit yield angegeben wurden, return oder throw (im Falle eines Fehlers), der Rest der Funktion Text ausgeführt wird und das zurückgegebene Objekt den value-Wert undefined und den done-Wert hat Eigenschaft von true:


function* myGeneratorFunction() {
    console
.log( "Start of the generator function." );
   
yield "First";
    console
.log( "Second part of the generator function."  );
   
yield "Second";
    console
.log( "Third part of the generator function." );
   
yield "Third";
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject
.next();
> "Start of the generator function."
> Object { value: "First", done: false }

myGeneratorObject
.next();
> "Second part of the generator function."
> Object { value: "Second", done: false }

myGeneratorObject
.next();
> "Third part of the generator function."
> Object { value: "Third", done: false }

myGeneratorObject
.next();
> Object { value: undefined, done: true }

Verwenden Sie next() nur für das Objekt, das die Generatorfunktion zurückgibt, nicht für das Generatorfunktion selbst. Andernfalls führt jeder Aufruf der Generatorfunktion Erstellt ein neues Generatorobjekt:

function* myGeneratorFunction() {
 
yield "First";
 
yield "Second";
};

myGeneratorFunction
().next();
> Object { value: "First", done: false }

myGeneratorFunction
().next();
> Object { value: "First", done: false }

Wie bei jeder Funktion wird die Generatorfunktion angehalten, wenn sie auf ein return-Objekt stößt. Keyword. Anschließend wird ein Objekt an den aufrufenden Kontext zurückgegeben, das die zurückgegebener Wert und eine done-Eigenschaft mit dem Wert true.

function* myGeneratorFunction() {
 
yield 1;
 
yield 2;
 
return 3;
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject
.next().done;
> Object { value: 1, done: false }

myGeneratorObject
.next().done;
> Object { value: 2, done: false }

myGeneratorObject
.next();
> Object { value: 3, done: true }

Ein yield-Ausdruck kann einen Teil der Semantik einer Kennung übernehmen, sodass eine wechselseitige "Kommunikation" und zurück zum gesperrten Teil Generatorfunktion erzeugen. Wenn ein Wert als next()-Methode eines Generators übergeben wird: ein Argument enthält, ersetzt es den Wert, der mit dem vorherigen, gesperrten yield-Ausdruck:

function* myGeneratorFunction() {
   
const firstYield = yield;
   
yield firstYield + 10;
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject
.next();
> Object { value: undefined, done: false }

myGeneratorObject
.next( 5 );
> Object { value: 15, done: false }

Beachten Sie, dass dabei der gesamte Ausdruck ersetzt wird, der mit dem vorherigen yield und weist nicht nur den Wert der vorherigen yield den in next() angegebenen Wert:

function* myGeneratorFunction() {
   
const firstYield = yield;
   
const secondYield = yield firstYield + 100;
   
yield secondYield + 10;
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject
.next();
> Object { value: undefined, done: false }

myGeneratorObject
.next( 10 ); // Can be thought of as changing the value of the `firstYield` variable to `10
> Object { value: 110, done: false }

myGeneratorObject
.next( 20 ); // Can be thought of as changing the value of the `secondYield` variable to `20`, _not_ `20 + 100;`
> Object { value: 30, done: false }

Alle Argumente, die an den ersten Aufruf von next() übergeben werden, werden ignoriert, da es vorherigen yield-Ausdruck, um diesen Wert zu akzeptieren. Wie bei jeder anderen Funktion Argumente, die an den ersten Aufruf der Generatorfunktion übergeben werden, sind im gesamten Umfang des Hauptteils der Generatorfunktion:

function* myGeneratorFunction( startingValue ) {
    let newValue
= yield startingValue + 1;
    newValue
= yield newValue + 10;
   
yield startingValue + 20;
};
const myGeneratorObject = myGeneratorFunction( 2 );

myGeneratorObject
.next( 1 );
> Object { value: 3, done: false }

myGeneratorObject
.next( 5 );
> Object { value: 15, done: false }

myGeneratorObject
.next( 10 );
Object { value: 22, done: false }

Der Operator yield* (beachten Sie das Sternchen) wird mit einem iterierbaren Wert verwendet, z. B. eine weitere Generatorfunktion, um über jeden Wert zu iterieren und seinen Operanden zu erhalten. gibt Folgendes zurück:

function* mySecondaryGenerator() {
 
yield 2;
 
yield 3;
}

function* myGenerator() {
 
yield 1;
 
yield* mySecondaryGenerator();
 
yield 4;
 
return 5;
}

const myIterator = myGenerator();

myIterator
.next();
> Object { value: 1, done: false }

myIterator
.next();
> Object { value: 2, done: false }

myIterator
.next();
> Object { value: 3, done: false }

myIterator
.next();
> Object { value: 4, done: false }

myIterator
.next();
> Object { value: 5, done: true }

Asynchrones JavaScript

Obwohl JavaScript grundsätzlich synchron ist, gibt es Mechanismen, die es den Entwicklern ermöglichen, die Ereignisschleife, asynchrone Aufgaben.

Promise-Objekte

Ein Promise ist ein Platzhalter für einen Wert, der nicht bekannt ist, wenn das Versprechen erstellt. Es ist ein Container, der einen asynchronen Vorgang vorgibt, die Begriffe der der als Erfolg oder Misserfolg gilt, die auszuführenden Aktionen und dem sich daraus ergebenden Wert.

Promise-Instanz mit dem Operator new und dem integrierten Promise erstellen -Konstruktor. Dieser Konstruktor akzeptiert die Funktion Executor als Argument verwenden. Diese Executor-Funktion wird in der Regel dazu verwendet, eine oder mehrere asynchrone Aktionen ein und geben dann die Begriffe vor, nach denen das Promise als erfolgreich erfüllt oder abgelehnt gelten. Ein Promise wird als ausstehend definiert. während die Executor-Funktion ausgeführt wird. Nach Abschluss des Executors als erfüllt (oder in einigen Dokumentationsquellen behoben) gilt, wenn die Executor-Funktion und die auszuführende asynchrone Aktion abgeschlossen sind. erfolgreich und abgelehnt, wenn bei der Executor-Funktion ein Fehler auftritt die asynchrone Aktion schlägt fehl. Nachdem ein Versprechen erfüllt oder abgelehnt wurde, gilt der Betrag als beseitigt.

const myPromise = new Promise( () => { });

Der Konstruktor ruft die Executor-Funktion mit zwei Argumenten auf. Diese Argumente sind Funktionen, mit denen Sie das Versprechen manuell erfüllen oder ablehnen können:

const  myPromise = new Promise( ( fulfill, reject ) => { });

Die Funktionen, die zur Erfüllung oder Ablehnung eines Versprechens verwendet werden, werden mit dem resultierenden des Promise als Argument (normalerweise ein Fehler bei einer Ablehnung):

const myPromise = new Promise( ( fulfill, reject ) => {
 
const myResult = true;
  setTimeout
(() => {
   
if( myResult === true ) {
        fulfill
( "This Promise was successful." );    
   
} else {
        reject
( new Error( "This Promise has been rejected." ) );
   
}
 
}, 10000);
});

myPromise
;
> Promise { <state>: "pending" }

myPromise
;
> Promise { <state>: "fulfilled", <value>: "This Promise was successful." }

Verkettung von Wertversprechen

Das resultierende Promise-Objekt kann mithilfe von then(), catch() und finally()-Methoden, die vom Promise-Konstruktor übernommen wurden. Jede dieser Optionen -Methoden gibt ein Promise zurück, auf das sofort mit then(), catch() oder finally(), sodass Sie die resultierenden Promise-Objekte verketten.

then() bietet zwei Callback-Funktionen als Argumente. Verwenden Sie die erste das sich daraus ergebende Versprechen und das zweite Versprechen abzulehnen. Beide Methoden akzeptieren einen , das dem resultierenden Promise seinen Wert zurückgibt.

const myPromise = new Promise( ( fulfill, reject ) => {
 
const myResult = true;
  setTimeout
(() => {
   
if( myResult === true ) {
        fulfill
( "This Promise was fulfilled." );    
   
} else {
        reject
( new Error( "This Promise has been rejected." ) );
   
}
 
}, 100);
});

myPromise
.then( successfulResult => console.log( successfulResult ), failedResult => console.error( failedResult ) );
> "This Promise was successful."

Sie können auch then() verwenden, um nur den erfüllten Status zu verarbeiten, und catch, um den Status „Abgelehnt“ bearbeiten. Rufen Sie catch mit einem einzelnen Argument auf, das den Parameter Wert, der in der Ablehnungsmethode des Promise angegeben wurde:

const myPromise = new Promise( ( fulfill, reject ) => {
 
const myResult = false;
  setTimeout
(() => {
   
if( myResult === true ) {
        fulfill
( "This Promise was fulfilled." );    
   
} else {
        reject
( new Error( "This Promise has been rejected." ) );
   
}
 
}, 100);
});

myPromise
 
.then( fulfilledResult => console.log(fulfilledResult ) )
 
.catch( rejectedResult => console.log( rejectedResult ) )
 
.finally( () => console.log( "The Promise has settled." ) );
> "Error: This Promise has been rejected."
> "The Promise has settled."

Im Gegensatz zu then und catch, mit denen eine Handler-Funktion ausgeführt werden kann, wenn ein Promise-Objekt erfüllt oder abgelehnt wurde, wird eine Funktion als Argument an finally übergeben. wird unabhängig davon aufgerufen, ob das Promise erfüllt oder abgelehnt wurde. Die Handler-Funktion wird ohne Argumente aufgerufen, da sie nicht dazu gedacht ist, mit den vom Promise übergebenen Werten arbeiten, nur um Code nach der Das Versprechen ist vollständig.

Gleichzeitigkeit

Der Promise-Konstruktor bietet vier Methoden für die Arbeit mit mehreren verwandten Promise-Objekte mithilfe eines iterables, das Promise-Objekte enthält. Diese Jede Methode gibt ein Versprechen zurück, das je nach Bundesstaat erfüllt oder abgelehnt wird. der an sie übergebenen Promise-Objekte. Mit Promise.all() wird beispielsweise ein Promise erstellt. die nur erfüllt wird, wenn jedes an diese Methode übergebene Versprechen erfüllt wird:

const firstPromise  = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const secondPromise = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const thirdPromise  = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const failedPromise = new Promise( ( fulfill, reject ) => reject( "Failed.") );
const successfulPromises = [ firstPromise, secondPromise, thirdPromise ];
const oneFailedPromise = [ failedPromise, ...successfulPromises ];

Promise.all( successfulPromises )
 
.then( ( allValues ) => {
    console
.log( allValues );
 
})
 
.catch( ( failValue ) => {
    console
.error( failValue );
 
});
> Array(3) [ "Successful. ", "Successful. ", "Successful. " ]

Promise.all( oneFailedPromise  )
   
.then( ( allValues ) => {
      console
.log( allValues );
   
})
   
.catch( ( failValue ) => {
     console
.error( failValue );
   
});
> "Failed."

Die Promise-Methoden für die Gleichzeitigkeit sind folgende:

Promise.all()
Wird nur dann erfüllt, wenn alle angegebenen Promises erfüllt sind.
Promise.any()
Erfüllt, wenn eines der angegebenen Promises erfüllt, aber nur abgelehnt wird wenn alle Promise-Objekte abgelehnt werden.
Promise.allSettled()
Wird erfüllt, wenn sich Versprechen entschieden haben, unabhängig von ihrem Ergebnis.
Promise.race()
Aufgrund des Ergebnisses des ersten Zahlungsversprechens abgelehnt oder erfüllt und ignoriert alle Promise-Objekte, die später geklärt wurden.

async/await

Wenn Sie das Schlüsselwort async vor einer Funktionsdeklaration verwenden oder Funktionsausdruck, einen beliebigen Wert, den die Funktion zurückgibt, als erfülltes Promise zurückgegeben wird, das Folgendes enthält: Wert. So können Sie asynchrone Vorgänge mit denselben Workflows als synchrone Entwicklung.

async function myFunction() {
 
return "This is my returned value.";
}

myFunction
().then( myReturnedValue => console.log( myReturnedValue ) );
> "This is my returned value."

Der Ausdruck await pausiert die Ausführung einer asynchronen Funktion, während das zugehörige Versprechen abgewickelt ist. Nach der Erfüllung des Versprechens Der Ausdruck await ist der erfüllte oder abgelehnte Wert des Versprechens.

async function myFunction() {
 
const myPromise  = new Promise( ( fulfill, reject ) => { setTimeout( () => fulfill( "Successful. "), 5000 ); });
 
const myPromisedResult = await myPromise;
 
return myPromisedResult;
}

myFunction
()
 
.then( myResult => console.log( myResult ) )
 
.catch( myFailedResult => console.error( myFailedResult ) );
> "Successful."

Alle in einem await-Ausdruck enthaltenen Nicht-Promise-Werte werden als Versprechen:

async function myFunction() {
 
const myPromisedResult = await "String value.";
 
return myPromisedResult;
}

myFunction
()
 
.then( myResult => console.log( myResult ) )
 
.catch( myFailedResult => console.error( myFailedResult ) );
> "String value."

Wissen testen

Welche Art von Schleife verwenden Sie, um über eine bekannte Menge zu iterieren?

for
do...while
while