JavaScript für ungeduldige Programmierer (ES2022-Ausgabe)
Bitte unterstützen Sie dieses Buch: kaufen Sie es oder spenden Sie
(Werbung, bitte nicht blockieren.)

23 Kontrollflussanweisungen



Dieses Kapitel behandelt die folgenden Kontrollflussanweisungen

23.1 Kontrollieren von Schleifen: break und continue

Die beiden Operatoren break und continue können verwendet werden, um Schleifen und andere Anweisungen zu kontrollieren, während wir uns darin befinden.

23.1.1 break

Es gibt zwei Versionen von break: eine mit einem Operanden und eine ohne. Die letztere Version funktioniert innerhalb der folgenden Anweisungen: while, do-while, for, for-of, for-await-of, for-in und switch. Sie verlässt sofort die aktuelle Anweisung

for (const x of ['a', 'b', 'c']) {
  console.log(x);
  if (x === 'b') break;
  console.log('---')
}

// Output:
// 'a'
// '---'
// 'b'

23.1.2 break mit Label: Verlassen jeder beschrifteten Anweisung

break mit einem Operanden funktioniert überall. Sein Operand ist ein Label. Labels können vor jede Anweisung gesetzt werden, einschließlich Blöcken. break my_label verlässt die Anweisung, deren Label my_label ist

my_label: { // label
  if (condition) break my_label; // labeled break
  // ···
}

Im folgenden Beispiel kann die Suche entweder

function findSuffix(stringArray, suffix) {
  let result;
  search_block: {
    for (const str of stringArray) {
      if (str.endsWith(suffix)) {
        // Success:
        result = str;
        break search_block; // (A)
      }
    } // for
    // Failure:
    result = '(Untitled)'; // (B)
  } // search_block

  return { suffix, result };
    // Same as: {suffix: suffix, result: result}
}
assert.deepEqual(
  findSuffix(['notes.txt', 'index.html'], '.html'),
  { suffix: '.html', result: 'index.html' }
);
assert.deepEqual(
  findSuffix(['notes.txt', 'index.html'], '.mjs'),
  { suffix: '.mjs', result: '(Untitled)' }
);

23.1.3 continue

continue funktioniert nur innerhalb von while, do-while, for, for-of, for-await-of und for-in. Es verlässt sofort die aktuelle Schleifeniteration und fährt mit der nächsten fort – zum Beispiel

const lines = [
  'Normal line',
  '# Comment',
  'Another normal line',
];
for (const line of lines) {
  if (line.startsWith('#')) continue;
  console.log(line);
}
// Output:
// 'Normal line'
// 'Another normal line'

23.2 Bedingungen von Kontrollflussanweisungen

if, while und do-while haben Bedingungen, die prinzipiell boolesch sind. Eine Bedingung muss jedoch nur truthy (true sein, wenn zu einem booleschen Wert konvertiert) sein, um akzeptiert zu werden. Mit anderen Worten, die folgenden beiden Kontrollflussanweisungen sind äquivalent

if (value) {}
if (Boolean(value) === true) {}

Dies ist eine Liste aller falsy Werte

Alle anderen Werte sind truthy. Weitere Informationen finden Sie unter §15.2 „Falsy und truthy Werte“.

23.3 if-Anweisungen [ES1]

Dies sind zwei einfache if-Anweisungen: eine mit nur einem „then“-Zweig und eine mit sowohl einem „then“-Zweig als auch einem „else“-Zweig

if (cond) {
  // then branch
}

if (cond) {
  // then branch
} else {
  // else branch
}

Anstelle des Blocks kann auf else auch eine weitere if-Anweisung folgen

if (cond1) {
  // ···
} else if (cond2) {
  // ···
}

if (cond1) {
  // ···
} else if (cond2) {
  // ···
} else {
  // ···
}

Sie können diese Kette mit weiteren else ifs fortsetzen.

23.3.1 Der Aufbau von if-Anweisungen

Der allgemeine Aufbau von if-Anweisungen ist

if (cond) «then_statement»
else «else_statement»

Bisher war die then_statement immer ein Block, aber wir können jede beliebige Anweisung verwenden. Diese Anweisung muss mit einem Semikolon abgeschlossen werden

if (true) console.log('Yes'); else console.log('No');

Das bedeutet, dass else if kein eigener Konstrukt ist; es ist einfach eine if-Anweisung, deren else_statement eine weitere if-Anweisung ist.

23.4 switch-Anweisungen [ES3]

Eine switch-Anweisung sieht wie folgt aus

switch («switch_expression») {
  «switch_body»
}

Der Körper von switch besteht aus null oder mehr case-Klauseln

case «case_expression»:
  «statements»

Und optional eine default-Klausel

default:
  «statements»

Eine switch wird wie folgt ausgeführt

23.4.1 Ein erstes Beispiel für eine switch-Anweisung

Betrachten wir ein Beispiel: Die folgende Funktion konvertiert eine Zahl von 1–7 in den Namen eines Wochentags.

function dayOfTheWeek(num) {
  switch (num) {
    case 1:
      return 'Monday';
    case 2:
      return 'Tuesday';
    case 3:
      return 'Wednesday';
    case 4:
      return 'Thursday';
    case 5:
      return 'Friday';
    case 6:
      return 'Saturday';
    case 7:
      return 'Sunday';
  }
}
assert.equal(dayOfTheWeek(5), 'Friday');

23.4.2 Vergessen Sie nicht return oder break!

Am Ende einer case-Klausel wird die Ausführung mit der nächsten case-Klausel fortgesetzt, es sei denn, wir return oder break – zum Beispiel

function englishToFrench(english) {
  let french;
  switch (english) {
    case 'hello':
      french = 'bonjour';
    case 'goodbye':
      french = 'au revoir';
  }
  return french;
}
// The result should be 'bonjour'!
assert.equal(englishToFrench('hello'), 'au revoir');

Das heißt, unsere Implementierung von dayOfTheWeek() funktionierte nur, weil wir return verwendet haben. Wir können englishToFrench() reparieren, indem wir break verwenden

function englishToFrench(english) {
  let french;
  switch (english) {
    case 'hello':
      french = 'bonjour';
      break;
    case 'goodbye':
      french = 'au revoir';
      break;
  }
  return french;
}
assert.equal(englishToFrench('hello'), 'bonjour'); // ok

23.4.3 Leere case-Klauseln

Die Anweisungen einer case-Klausel können weggelassen werden, was uns effektiv mehrere case-Ausdrücke pro case-Klausel gibt

function isWeekDay(name) {
  switch (name) {
    case 'Monday':
    case 'Tuesday':
    case 'Wednesday':
    case 'Thursday':
    case 'Friday':
      return true;
    case 'Saturday':
    case 'Sunday':
      return false;
  }
}
assert.equal(isWeekDay('Wednesday'), true);
assert.equal(isWeekDay('Sunday'), false);

23.4.4 Prüfen auf ungültige Werte über eine default-Klausel

Eine default-Klausel wird angesprungen, wenn der switch-Ausdruck keine andere Übereinstimmung hat. Das macht sie nützlich für die Fehlerprüfung

function isWeekDay(name) {
  switch (name) {
    case 'Monday':
    case 'Tuesday':
    case 'Wednesday':
    case 'Thursday':
    case 'Friday':
      return true;
    case 'Saturday':
    case 'Sunday':
      return false;
    default:
      throw new Error('Illegal value: '+name);
  }
}
assert.throws(
  () => isWeekDay('January'),
  {message: 'Illegal value: January'});

  Übungen: switch

23.5 while-Schleifen [ES1]

Eine while-Schleife hat den folgenden Aufbau

while («condition») {
  «statements»
}

Vor jeder Schleifeniteration wertet while die condition aus

23.5.1 Beispiele für while-Schleifen

Der folgende Code verwendet eine while-Schleife. In jeder Schleifeniteration entfernt er das erste Element von arr mit .shift() und gibt es aus.

const arr = ['a', 'b', 'c'];
while (arr.length > 0) {
  const elem = arr.shift(); // remove first element
  console.log(elem);
}
// Output:
// 'a'
// 'b'
// 'c'

Wenn die Bedingung immer zu true ausgewertet wird, ist while eine Endlosschleife

while (true) {
  if (Math.random() === 0) break;
}

23.6 do-while-Schleifen [ES3]

Die do-while-Schleife funktioniert ähnlich wie while, aber sie prüft ihre Bedingung nach jeder Schleifeniteration, nicht davor.

let input;
do {
  input = prompt('Enter text:');
  console.log(input);
} while (input !== ':q');

do-while kann auch als while-Schleife betrachtet werden, die mindestens einmal durchläuft.

prompt() ist eine globale Funktion, die in Webbrowsern verfügbar ist. Sie fordert den Benutzer auf, Text einzugeben und gibt ihn zurück.

23.7 for-Schleifen [ES1]

Eine for-Schleife hat den folgenden Aufbau

for («initialization»; «condition»; «post_iteration») {
  «statements»
}

Die erste Zeile ist der Kopf der Schleife und steuert, wie oft der Körper (der Rest der Schleife) ausgeführt wird. Er hat drei Teile, und jeder davon ist optional

Eine for-Schleife ist daher ungefähr äquivalent zur folgenden while-Schleife

«initialization»
while («condition») {
  «statements»
  «post_iteration»
}

23.7.1 Beispiele für for-Schleifen

Als Beispiel hier, wie man mit einer for-Schleife von Null bis Zwei zählt

for (let i=0; i<3; i++) {
  console.log(i);
}

// Output:
// 0
// 1
// 2

So gibt man den Inhalt eines Arrays mit einer for-Schleife aus

const arr = ['a', 'b', 'c'];
for (let i=0; i<arr.length; i++) {
  console.log(arr[i]);
}

// Output:
// 'a'
// 'b'
// 'c'

Wenn wir alle drei Teile des Kopfes weglassen, erhalten wir eine Endlosschleife

for (;;) {
  if (Math.random() === 0) break;
}

23.8 for-of-Schleifen [ES6]

Eine for-of-Schleife iteriert über jedes iterable – ein Datencontainer, der das Iterationsprotokoll unterstützt. Jeder iterierte Wert wird in einer Variablen gespeichert, wie im Kopf angegeben

for («iteration_variable» of «iterable») {
  «statements»
}

Die Iterationsvariable wird normalerweise über eine Variablendeklaration erstellt

const iterable = ['hello', 'world'];
for (const elem of iterable) {
  console.log(elem);
}
// Output:
// 'hello'
// 'world'

Aber wir können auch eine (veränderliche) Variable verwenden, die bereits existiert

const iterable = ['hello', 'world'];
let elem;
for (elem of iterable) {
  console.log(elem);
}

23.8.1 const: for-of vs. for

Beachten Sie, dass wir in for-of-Schleifen const verwenden können. Die Iterationsvariable kann für jede Iteration immer noch unterschiedlich sein (sie kann nur während der Iteration nicht geändert werden). Stellen Sie es sich wie eine neue const-Deklaration vor, die jedes Mal in einem neuen Gültigkeitsbereich ausgeführt wird.

Im Gegensatz dazu müssen wir in for-Schleifen Variablen über let oder var deklarieren, wenn sich ihre Werte ändern.

23.8.2 Iterieren über Iterables

Wie bereits erwähnt, funktioniert for-of mit jedem iterable Objekt, nicht nur mit Arrays – zum Beispiel mit Sets

const set = new Set(['hello', 'world']);
for (const elem of set) {
  console.log(elem);
}

23.8.3 Iterieren über [Index, Element]-Paare von Arrays

Zuletzt können wir for-of auch verwenden, um über die [Index, Element]-Einträge von Arrays zu iterieren

const arr = ['a', 'b', 'c'];
for (const [index, elem] of arr.entries()) {
  console.log(`${index} -> ${elem}`);
}
// Output:
// '0 -> a'
// '1 -> b'
// '2 -> c'

Mit [index, element] verwenden wir Destrukturierung, um auf Array-Elemente zuzugreifen.

  Übung: for-of

exercises/control-flow/array_to_string_test.mjs

23.9 for-await-of-Schleifen [ES2018]

for-await-of ist wie for-of, aber es arbeitet mit asynchronen Iterables statt mit synchronen. Und es kann nur innerhalb von asynchronen Funktionen und asynchronen Generatoren verwendet werden.

for await (const item of asyncIterable) {
  // ···
}

for-await-of wird detailliert im Kapitel über asynchrone Iteration beschrieben.

23.10 for-in-Schleifen (vermeiden) [ES1]

Die for-in-Schleife durchläuft alle (eigenen und geerbten) aufzählbaren Eigenschaftsschlüssel eines Objekts. Beim Schleifen über ein Array ist sie selten eine gute Wahl

Der folgende Code demonstriert diese Punkte

const arr = ['a', 'b', 'c'];
arr.propKey = 'property value';

for (const key in arr) {
  console.log(key);
}

// Output:
// '0'
// '1'
// '2'
// 'propKey'

23.11 Empfehlungen für Schleifen

  Quiz

Siehe Quiz-App.