Funktionen und Closures in JavaScript verstehen

Lerne, JavaScript-Code in wiederverwendbare Funktionen zu fassen. Schreibe Funktionsdeklarationen, Funktionsausdrücke und Pfeilfunktionen; übergib Werte mithilfe von Parametern, Standardwerten und Restparametern und gib Ergebnisse mit return zurück. Bestimme anhand von Blockgültigkeitsbereich, Funktionsgültigkeitsbereich und globalem Gültigkeitsbereich, wo eine Variable verwendet werden kann. Gib Funktionen mit Closures ein eigenes Gedächtnis und übergib Funktionen als Callbacks an andere Funktionen, um flexiblen Code mit Funktionen höherer Ordnung zu schreiben.

AnfängerEinheit 3 von 9 in Kurs: JavaScript – DOM, Ereignisse und asynchroner Code

Was du lernst

  1. Fasse ein wiederverwendbares Stück Code als Funktion zusammen, rufe es mit Argumenten auf und wähle zwischen Funktionsdeklaration, Funktionsausdruck und Pfeilfunktion.
    • Schreibe eine Funktionsdeklaration und rufe die Funktion auf
      Definiere mit dem Schlüsselwort function eine benannte Funktion, rufe sie auf und erkläre, warum Funktionen Code kürzer, übersichtlicher und leichter änderbar machen.
    • Speichere eine Funktion mit einem Funktionsausdruck in einer Variablen
      Behandle Funktionen als Werte: Erstelle einen Funktionsausdruck, weise ihn einer Variablen zu und erkläre, wie er sich bei Hoisting und Benennung von einer Deklaration unterscheidet.
    • Schreibe Pfeilfunktionen
      Nutze die kurze Pfeilsyntax, erkenne, wann eine Pfeilfunktion automatisch einen Wert zurückgibt, und erkläre, wie sie sich von Funktionen unterscheidet, die mit function geschrieben werden.
  2. Gestalte, was in eine Funktion hineingeht und was herauskommt: Ordne Argumente Parametern zu, lege Standardwerte fest, nimm beliebig viele Argumente entgegen, gib Ergebnisse zurück und verfolge ineinander verschachtelte Aufrufe.
    • Ordne Argumente den Parametern zu
      Erkläre, wie die Werte eines Aufrufs zu den Parametern der Funktion gelangen, was bei zu wenigen oder zu vielen Argumenten geschieht und welche Daten des aufrufenden Codes eine Funktion ändern kann.
    • Lege Standardwerte fest und nimm beliebig viele Argumente entgegen
      Gib Parametern Standardwerte, sammle beliebig viele Argumente mit einem Restparameter und entpacke ein Array in einzelne Argumente.
    • Gib Ergebnisse aus einer Funktion zurück
      Gib mit return einen Wert zurück, verwende das Ergebnis im weiteren Code und unterscheide die Rückgabe eines Werts von seiner Ausgabe oder einer Änderung außerhalb der Funktion.
    • Verfolge Funktionsaufrufe im Aufrufstapel
      Verfolge die Reihenfolge, in der verschachtelte Aufrufe ausgeführt werden und zurückkehren, und schreibe eine einfache rekursive Funktion mit einem Basisfall.
  3. Bestimme mithilfe der Regeln für Blockgültigkeitsbereich, Funktionsgültigkeitsbereich und globalen Gültigkeitsbereich sowie für Funktionen innerhalb anderer Funktionen, wo eine Variable sichtbar ist.
    • Unterscheide Blockgültigkeitsbereich, Funktionsgültigkeitsbereich und globalen Gültigkeitsbereich
      Erkläre den Gültigkeitsbereich einer mit let, const oder var deklarierten Variablen und nutze lokale Variablen, um Funktionen voneinander unabhängig zu halten.
    • Bestimme, auf welche Variable ein Name verweist
      Verfolge die Gültigkeitsbereichskette, um die Variable hinter einem Namen zu finden, erkenne Verdeckung und erkläre Hoisting und die zeitliche Totzone.
  4. Erkläre, wie eine Funktion nach dem Ende der äußeren Funktion Zugriff auf die sie umgebenden Variablen behält, und nutze dies, um Funktionsfabriken und privaten Zustand zu erstellen.
    • Erkläre, was eine Closure behält
      Beschreibe, wie eine Funktion Zugriff auf die Variablen des Gültigkeitsbereichs behält, in dem sie erzeugt wurde, selbst nachdem die äußere Funktion zurückgekehrt ist.
    • Nutze Closures für Funktionsfabriken und privaten Zustand
      Erstelle Funktionsfabriken, halte Daten hinter Funktionen privat und vermeide den klassischen Closure-Fehler in einer Schleife.
  5. Nutze Funktionen als Werte: Übergib sie als Callbacks an andere Funktionen, nutze und schreibe Funktionen höherer Ordnung und erstelle neue Funktionen aus vorhandenen.
    • Übergib einer Funktion einen Callback
      Übergib eine Funktion als Argument an eine andere Funktion, schreibe Funktionen, die einen Callback aufrufen, und übergib die Funktion selbst statt ihres Ergebnisses.
    • Nutze und schreibe Funktionen höherer Ordnung
      Erkenne Funktionen höherer Ordnung, nutze Arraymethoden, die Callbacks entgegennehmen, und schreibe eine eigene Funktion, die eine Funktion als Eingabe entgegennimmt.
    • Erstelle neue Funktionen aus vorhandenen Funktionen
      Schreibe Funktionen höherer Ordnung, die andere Funktionen umhüllen, kombinieren oder deren Argumente vorbelegen. Nutze Closures, damit sie sich die übergebene Funktion merken.

Funktionen und Closures in JavaScript einfach erklärt

5 Kapitel

1

Definiere Funktionen in drei Formen und rufe sie auf

Funktionen machen aus wiederholten Anweisungen benannte Aufgaben. Du wirst drei gebräuchliche Formen lesen und schreiben und dann entscheiden, ob eine Zeile eine Funktion erzeugt, speichert oder aufruft. Kleine Unterschiede in der Syntax sind wichtig: Eine Deklaration ist früh verfügbar, während ein gespeicherter Ausdruck zuerst initialisiert werden muss. Bei einem Pfeilfunktionsrumpf kann die Form darüber entscheiden, ob ein Ergebnis zurückgegeben wird. Diese Unterschiede bereiten dich darauf vor, Funktionen als gewöhnliche Werte weiterzugeben.

1.1 Schreibe eine Funktionsdeklaration und rufe die Funktion auf

Eine Funktion gibt einer Aufgabe einen Namen und speichert die Anweisungen zu ihrer Ausführung. Das Definieren führt diese Anweisungen nicht aus. Ein Aufruf führt sie aus und ein weiterer Aufruf führt sie mit neuen Eingaben erneut aus.

Schreibe die Definition

Eine Deklaration enthält das Schlüsselwort function, einen Namen, runde Klammern für Parameter und geschweifte Klammern für den Rumpf:

function greet(name) {
  console.log("Hello, " + name + "!");
}

Hier ist name ein Parameter: ein lokaler Name für die Eingabe, die jeder Aufruf liefert. Der Rumpf verbindet diese Eingabe mit dem umgebenden Text und gibt das Ergebnis aus. Die schließende geschweifte Klammer markiert das Ende der Definition; die Ausführung wird mit der nächsten Anweisung außerhalb fortgesetzt.

Auch eine Funktion ohne Eingaben braucht runde Klammern. Beispielsweise definiert function sayHi() { console.log("Hi"); } eine Aufgabe mit leerer Parameterliste. Die geschweiften Klammern gehören zur Definition; die runden Klammern nach einem Namen stehen sowohl in Definitionen als auch in Aufrufen und haben dort unterschiedliche Aufgaben.

Führe die Aufgabe aus
greet("Amira");
greet("Li");

Der erste Aufruf gibt name den Wert "Amira" und gibt „Hello, Amira!“ aus. Der zweite beginnt einen neuen Aufruf mit name gleich "Li" und gibt „Hello, Li!“ aus. Die Definition wird gemeinsam genutzt; die Eingabe gehört zum jeweiligen Aufruf.

greet allein verweist auf den Funktionswert. Es führt den Rumpf nicht aus. Dieser Unterschied ist wichtig, wenn eine Zeile scheinbar nichts bewirkt: sayHi; liest einen Funktionswert und verwirft ihn, während sayHi(); „Hi“ ausgibt.

Ersetze kopierte Anweisungen

Angenommen, mehrere Berichte benötigen eine Überschrift zwischen Trennlinien:

function printHeading(title) {
  console.log("-----");
  console.log(title);
  console.log("-----");
}

printHeading("Report");
printHeading("Totals");

Der erste Aufruf gibt eine Linie, „Report“ und eine weitere Linie aus. Der zweite gibt denselben Rahmen um „Totals“ aus. Um die Art der Linien zu ändern, bearbeite die beiden Anweisungen zur Linienausgabe in dieser einen Definition. Ohne die Funktion müsste jede kopierte Überschrift einzeln korrigiert werden.

Wähle einen Namen, der beschreibt, was ein Aufruf tut. printHeading beschreibt eine Aktion; isValidEmail benennt eine Ja-Nein-Prüfung. Verwende camelCase, mit einem kleingeschriebenen ersten Wort und großgeschriebenen Anfängen der folgenden Wörter. Verbindet ein vorgeschlagener Name zwei getrennte Aufgaben mit „und“, überlege, jeder Aufgabe eine eigene Funktion zu geben.

Lies Code, der eine Deklaration früh aufruft

Deklarationen werden eingerichtet, bevor der umgebende Code ausgeführt wird. Dieses Verhalten heißt Hoisting:

sayHi();
function sayHi() {
  console.log("Hi");
}

Der Aufruf funktioniert innerhalb des Gültigkeitsbereichs der Deklaration, obwohl die geschriebene Definition später folgt. Hoisting führt den Rumpf nicht früher aus; es stellt die Funktion für einen Aufruf bereit.

Prüfe Schreibweise sowie Groß- und Kleinschreibung, wenn ein Aufruf fehlschlägt. Greet() und greet() sind verschiedene Namen. Der Aufruf eines nicht deklarierten greeet() löst einen ReferenceError aus; fehlen die Klammern des Aufrufs, kann der Fehler unbemerkt bleiben, weil das bloße Lesen einer vorhandenen Funktion gültiger Code ist.

Wichtige Erkenntnisse

  • Das Definieren einer Funktion speichert Anweisungen; ihr Aufruf führt sie aus.
  • Rufe Funktionen mit runden Klammern auf, auch wenn es keine Argumente gibt.
  • Eine Deklaration ist innerhalb desselben Gültigkeitsbereichs schon vor ihrer geschriebenen Position verfügbar.
  • Benenne eine klare Aufgabe, damit wiederholter Code an einer Stelle geändert werden kann.

1.2 Speichere eine Funktion mit einem Funktionsausdruck in einer Variablen

Funktionen sind Werte. Du kannst eine Funktion in einer Variablen speichern, ihre Referenz kopieren, sie an eine andere Funktion übergeben oder zurückgeben. Ein Funktionsausdruck erzeugt diesen Wert dort, wo ein Ausdruck erwartet wird.

Speichere eine Funktion
const square = function (n) {
  return n * n;
};

const answer = square(4);

Der Ausdruck nach = erzeugt eine Funktion. Die Zuweisung speichert sie in square; der spätere Aufruf gibt 16 zurück. Diese Anweisung endet entsprechend der hier verwendeten Formatierungskonvention mit einem Semikolon.

Die Position von function unterscheidet die Formen. Eine als eigenständiges function square(n) { ... } geschriebene Definition ist eine Deklaration. Eine Funktion nach = oder direkt als Argument eines Aufrufs ist ein Ausdruck. Beide gewöhnlichen Funktionsformen können Parameter entgegennehmen und Werte zurückgeben.

Verwende const für eine Funktionsvariable, wenn ihr Wert gleich bleiben soll. Der Aufruf square(5) ist erlaubt; die Zuweisung square = 5; löst einen TypeError aus. Die Konstante schützt die Variable vor dem Ersetzen. Wiederholte Aufrufe verhindert sie nicht.

Speichere eine Funktion oder ihr Ergebnis
const sq = square;
const result = square(3);

sq verweist auf dieselbe Funktion wie square. Der Aufruf sq(3) gibt 9 zurück. result enthält die Zahl 9, die ein Aufruf zurückgegeben hat. Sie kann nicht als Funktion aufgerufen werden. typeof square und typeof sq ergeben "function"; typeof result ergibt "number".

Es hilft, eine Zuweisung von rechts nach links zu lesen: Entscheide zuerst, ob ihre rechte Seite eine Funktion erzeugt, auf eine vorhandene Funktion verweist oder eine Funktion aufruft. Die Variable links erhält diesen Wert. Das Kopieren der Referenz erzeugt keine zweite unabhängige Implementierung.

Warte, bis die Zuweisung ausgeführt wird

Eine Deklaration und ein gespeicherter Ausdruck sind zu unterschiedlichen Zeiten aufrufbar:

FormVor der geschriebenen Definition oder Zuweisung
function square(n) { return n * n; }Innerhalb ihres Gültigkeitsbereichs aufrufbar
const square = function (n) { return n * n; };Das Lesen von square löst einen ReferenceError aus
var square = function (n) { return n * n; };square ist undefined; ein Aufruf löst einen TypeError aus

Bei const oder let kann der Name vor der Initialisierung nicht verwendet werden. Bei var existiert der Name früh, der Funktionswert wird aber erst zugewiesen, wenn die Ausführung den Ausdruck erreicht. Das Hoisting der Variablen bewirkt kein Hoisting der Zuweisung.

Gib einem Ausdruck einen internen Namen
const factorial = function fact(n) {
  return n <= 1 ? 1 : n * fact(n - 1);
};

Der äußere Name ist factorial. Der Name fact gehört in die Funktion und ermöglicht ihr, sich selbst aufzurufen; Code außerhalb kann diesen internen Namen nicht direkt verwenden. Der Name des Ausdrucks kann auch Stacktraces leichter lesbar machen. Beispielsweise berechnet factorial(3) 3 mal 2 mal 1 und gibt 6 zurück.

Deklarationen eignen sich gut für die wichtigsten benannten Aufgaben in einem Skript. Ausdrücke passen an Stellen, an denen Code einen Funktionswert benötigt, etwa bei einer Variablenzuweisung oder einem Argument. Die Wahl eines Ausdrucks ändert, wie der Wert erzeugt und gespeichert wird, nicht aber, wie er mit runden Klammern aufgerufen wird.

Wichtige Erkenntnisse

  • Ein Funktionsausdruck erzeugt einen Funktionswert innerhalb eines anderen Ausdrucks.
  • Die Zuweisung von square kopiert eine Funktionsreferenz; die Zuweisung von square(3) speichert das Ergebnis.
  • Ein in const gespeicherter Funktionsausdruck ist erst nach der Initialisierung aufrufbar.
  • Eine Funktionsvariable mit var enthält undefined, bevor ihre Zuweisung ausgeführt wird.
  • Ein benannter Ausdruck kann seinen internen Namen für Selbstaufrufe verwenden.

1.3 Schreibe Pfeilfunktionen

Pfeilfunktionen sind Funktionsausdrücke mit einer kürzeren Schreibweise. Ihr Rückgabeverhalten hängt davon ab, was auf den Pfeil folgt. Sieh dir daher den Rumpf an, bevor du entscheidest, was ein Aufruf liefert.

Wähle die Parameterform
const sayHi = () => "hi";
const double = n => n * 2;
const add = (a, b) => a + b;

Ohne Parameter sind leere Klammern erforderlich. Bei einem einfachen Parameter dürfen die Klammern entfallen. Bei zwei oder mehr sind sie erforderlich. Auch ein Standard- oder Restparameter braucht Klammern: (n = 1) => n * 2 und (...nums) => nums.length sind zulässig.

Der Pfeil trennt die Eingaben vom Rumpf. const double = n => n * 2; erzeugt die Funktion und speichert sie; double(6) ruft sie später auf und gibt 12 zurück. Wie jede mit const gespeicherte Funktion aus einem Funktionsausdruck kann double nicht gelesen werden, bevor seine Initialisierungszeile ausgeführt wird.

Gib einen Ausdruck zurück oder schreibe einen Block
RumpfBeispielErgebnis des Aufrufs mit 2 und 3
Ausdrucksrumpf(a, b) => a + b5
Block mit return(a, b) => { return a + b; }5
Block ohne return(a, b) => { a + b; }undefined

Ein Ausdrucksrumpf gibt seinen Ausdruck automatisch zurück. Geschweifte Klammern beginnen einen Block aus Anweisungen. In einem Block gibt das Auswerten eines Ausdrucks ihn nicht an den aufrufenden Code zurück; schreibe return ausdrücklich.

Verwende einen Block, wenn die Aufgabe Zwischenschritte braucht:

const describe = (n) => {
  const kind = n % 2 === 0 ? "even" : "odd";
  return n + " is " + kind;
};

Für describe(7) ist die Bedingung falsch, sodass kind zu "odd" wird. Die Rückgabe verbindet dann die Zahl und die Wörter zu „7 is odd“. Würdest du diese Rückgabe entfernen, bliebe die lokale Berechnung bestehen, aber der aufrufende Code erhielte undefined.

Gib ein Objektliteral zurück

Ein Objektliteral verwendet ebenfalls geschweifte Klammern. Setze es in runde Klammern, damit der Parser es als Ausdruck behandelt:

const makePoint = (x, y) => ({ x: x, y: y });

makePoint(2, 5) gibt ein Objekt zurück, dessen x gleich 2 und dessen y gleich 5 ist. Ohne die runden Klammern beginnt die erste geschweifte Klammer einen Block. Die Form mit einer Eigenschaft () => { value: 1 } gibt undefined zurück; die ähnliche Form mit zwei Eigenschaften und einem Komma führt zu einem Syntaxfehler. Die runden Klammern ändern, wie der Rumpf gelesen wird.

Kenne die Grenzen der kürzeren Form

Pfeilfunktionen haben kein eigenes this oder arguments und können nicht mit new aufgerufen werden, um Objekte zu konstruieren. Bei einer einfachen Berechnung, die weder this noch arguments verwendet, verhalten sich eine Pfeilfunktion und ein gewöhnlicher Funktionsausdruck gleich. Ersetzt du jede gewöhnliche Funktion durch eine Pfeilfunktion, kann sich Code ändern, der von diesen Eigenschaften abhängt.

Kurze Pfeilfunktionen sind als Callbacks besonders gut lesbar. [1, 2, 3].map(n => n * 10) übergibt eine Funktion, die ein transformiertes Element zurückgibt, und erzeugt [10, 20, 30]. Änderst du sie zu n => { n * 10; }, geht jeder Rückgabewert unbemerkt verloren.

Wichtige Erkenntnisse

  • Ein Ausdrucksrumpf einer Pfeilfunktion gibt seinen Ausdruck automatisch zurück.
  • Ein Blockrumpf einer Pfeilfunktion braucht ein ausdrückliches return, um einen berechneten Wert zurückzugeben.
  • Setze ein Objektliteral, das ein Ausdrucksrumpf zurückgibt, in runde Klammern.
  • Leere und mehrere Parameter sowie Standard- und Restparameter brauchen Klammern.
  • Pfeilfunktionen haben kein eigenes this oder arguments und können keine Konstruktoren sein.
2

Übergib Funktionen Werte und erhalte Ergebnisse zurück

Ein Funktionsaufruf hat eine Eingabe- und eine Ausgabeseite. Ordne Argumente Parametern zu, verarbeite optionale oder flexible Eingaben und verfolge den Wert, der an den aufrufenden Code zurückgegeben wird. Du wirst außerdem lokale Änderungen von Änderungen an einem gemeinsam genutzten Array unterscheiden und verschachtelte Aufrufe verfolgen, während ihre Ausführung pausiert und fortgesetzt wird. Dasselbe Modell aus Aufruf und Rückkehr erklärt einfache Rekursion, bei der mehrere Aufrufe einer Funktion auf das Ende kleinerer Aufrufe warten.

2.1 Ordne Argumente den Parametern zu

Die Parameter einer Funktion geben den von einem Aufruf gelieferten Werten Namen. Um einen Aufruf nachzuvollziehen, ordne zuerst die Eingaben nach ihrer Position zu und verfolge dann die Operationen im Rumpf.

Ordne Positionen und Typen zu
function area(width, height) {
  return width * height;
}

area(4, 2.5);

width und height sind Parameter; 4 und 2,5 sind Argumente. Während dieses Aufrufs enthält width den Wert 4 und height den Wert 2,5, sodass das Ergebnis 10 ist. Parameternamen werden nicht mit Variablennamen des aufrufenden Codes abgeglichen. Bei divide(a, b) ändert das Vertauschen von divide(10, 2) zu divide(2, 10) das Ergebnis von 5 auf 0,2.

JavaScript schreibt die Anzahl der Argumente nicht vor. area(4) belässt height bei undefined; die Multiplikation damit ergibt NaN. area(4, 2, 99) gibt 8 zurück, weil dieser Rumpf das dritte Argument nicht verwendet. Eine Funktion kann zusätzliche Argumente sammeln, aber ungenutzte zusätzliche Argumente verursachen nicht automatisch einen Fehler.

Auch die Typen sind wichtig, selbst wenn der Aufruf akzeptiert wird. area("4", 2) gibt 8 zurück, weil die Multiplikation den Text umwandelt. Eine Additionsfunktion verhält sich anders: add("4", 2) gibt „42“ zurück, wenn ihr Rumpf return a + b; lautet. Stellt Text eine Zahl dar, gibt add(Number("4"), 2) den Wert 6 zurück.

Weise einem lokalen Parameter einen neuen Wert zu

Jeder Parameter ist lokal für seinen Aufruf. Weist du ihm eine neue Zahl zu, wird der Variablen des aufrufenden Codes kein neuer Wert zugewiesen:

let score = 5;
function reset(s) {
  s = 0;
}
reset(score);

Der Aufruf kopiert den Wert 5 in s. Die Zuweisung ändert s auf 0 und score bleibt 5. Zahlen, Zeichenketten und boolesche Werte folgen dieser Regel der Wertkopie. Code außerhalb kann s nicht direkt lesen; eine verschachtelte Funktion kann über eine Closure Zugriff darauf behalten.

Ändere ein Array über eine kopierte Referenz

Ein Arraywert ist eine Referenz auf ein Array. Beim Übergeben dieses Werts wird die Referenz kopiert, sodass der aufrufende Code und der Parameter auf dasselbe Array zugreifen können:

const list = [1, 2];
function addItem(arr) {
  arr.push(3);
}
addItem(list);

list ist nun [1, 2, 3]. Die Funktion hat list keinen neuen Wert zugewiesen; sie hat das Array geändert, auf das beide Namen zugreifen. Eine const-Deklaration verhindert das Ersetzen ihrer gespeicherten Referenz, nicht aber das Ändern des Arrayinhalts.

Vergleiche eine lokale Neuzuweisung:

function clearLocal(arr) {
  arr = [];
}
clearLocal(list);

Dadurch verweist nur arr auf ein neues leeres Array. list greift weiterhin auf [1, 2, 3] zu. Frag dich, ob eine Operation ein vorhandenes Array ändert, wie push, oder einem Namen einen anderen Wert zuweist, wie =.

Gib ein neues Array zurück, wenn die Eingabe erhalten bleiben soll
function withItem(arr, item) {
  return arr.concat(item);
}

withItem([1, 2], 3) erzeugt [1, 2, 3] als neues Array. Das Eingabearray bleibt [1, 2]. Das macht die Funktion nützlich, wenn der aufrufende Code sowohl die alte als auch die erweiterte Liste benötigt. Mache durch Namen und Verhalten einer Funktion deutlich, welche Änderungen sie an ihrer Eingabe vornehmen soll, damit aufrufender Code sie erwartungsgemäß nutzen kann.

Wichtige Erkenntnisse

  • Argumente werden Parametern nach ihrer Position zugeordnet.
  • Ein fehlendes Argument liefert undefined; ungenutzte zusätzliche Argumente sind erlaubt.
  • Eine Neuzuweisung an einen Parameter weist der Variablen des aufrufenden Codes keinen neuen Wert zu.
  • Eine Änderung an einem Array über einen Parameter ändert das gemeinsam genutzte Array.
  • Die Rückgabe eines neuen Arrays kann die Eingabe erhalten.

2.2 Lege Standardwerte fest und nimm beliebig viele Argumente entgegen

Standardwerte machen eine Eingabe optional; Restparameter erlauben es einem Aufruf, viele Eingaben zu übergeben. Die Spread-Syntax verbindet ein vorhandenes Array mit einer Funktion, die einzelne Argumente erwartet.

Verwende einen Standardwert nur bei undefined
function greet(name = "friend") {
  return "Hello, " + name;
}

Sowohl greet() als auch greet(undefined) geben „Hello, friend“ zurück. greet(null) gibt „Hello, null“ und greet("") gibt „Hello, “ zurück. Ein Standardwert prüft auf undefined, nicht auf jeden Wert, der leer aussieht.

Standardwerte werden ausgewertet, wenn ein Aufruf sie benötigt. Sie dürfen frühere Parameter verwenden: function makeBox(width, height = width) { return width * height; } gibt für makeBox(3) den Wert 9 und für makeBox(3, 4) den Wert 12 zurück. Das ausdrücklich angegebene zweite Argument ersetzt für diesen Aufruf den Standardwert.

Setze erforderliche Eingaben an den Anfang. Bei function total(amount, rate = 0.25) { return amount + amount * rate; } gibt total(80) den Wert 100 und total(80, 0.5) den Wert 120 zurück. Würden die Parameter vertauscht, wäre für den üblichen Aufruf mit Standardsatz total(undefined, 80) nötig.

Älterer Code verwendet möglicherweise level = level || 50;. Das ersetzt auch 0, false und eine leere Zeichenkette, weil sie Falsy-Werte sind. Ein Standardparameter erhält diese Werte: function setVolume(level = 50) { return level; } gibt für setVolume(0) den Wert 0 und für setVolume(undefined) den Wert 50 zurück.

Sammle Argumente mit Rest
function sum(...numbers) {
  let total = 0;
  for (const n of numbers) {
    total += n;
  }
  return total;
}

sum(1, 2, 3) legt [1, 2, 3] in numbers ab und gibt 6 zurück. sum() übergibt ein leeres Array, sodass die Schleife nichts addiert und 0 zurückgibt. Ein Restparameter ist ein echtes Array und kann direkt für Arraymethoden oder Schleifendurchläufe verwendet werden.

Eine feste Eingabe kann vor Rest stehen: In function log(label, ...values) { console.log(label, values); } gibt log("scores", 7, 9) dem Parameter label die Zeichenkette „scores“ und values das Array [7, 9]. Nur ein Restparameter ist erlaubt und er muss zuletzt stehen; andernfalls kann JavaScript die übrigen Argumente mit dieser Syntax nicht eindeutig zuordnen.

Entfalte Argumente mit Spread
Position von ...AufgabeBeispiel
Parameterliste der DefinitionÜbrige Argumente sammelnfunction sum(...numbers) { ... }
Argumentliste des AufrufsArrayelemente entfaltensum(...values)

Math.max(...[3, 7, 2]) erhält drei Zahlen und gibt 7 zurück. Math.max([3, 7, 2]) erhält ein Array und gibt NaN zurück. Die drei Punkte sehen im Code gleich aus, aber die umgebende Syntax zeigt dir, welche Aufgabe sie erfüllen.

Gewöhnliche Funktionen haben auch ein arguments-Objekt, das die an sie übergebenen Argumente enthält. Es ist arrayähnlich und hat indizierte Einträge und eine Länge, ist aber kein echtes Array und hat keine eigenen Methoden map oder filter. Pfeilfunktionen haben kein eigenes arguments. Bevorzuge für neuen Code, der eine flexible Liste von Eingaben benötigt, einen Restparameter.

Wichtige Erkenntnisse

  • Standardwerte gelten für fehlende Argumente und ausdrücklich übergebenes undefined, während 0 und null erhalten bleiben.
  • Ein Standardausdruck wird für jeden Aufruf ausgewertet, der ihn benötigt.
  • Ein Restparameter sammelt die übrigen Argumente in einem Array und steht zuletzt.
  • Spread entfaltet ein Array in einzelne Argumente eines Aufrufs.
  • Das ältere arguments-Objekt ist arrayähnlich; Rest erzeugt ein echtes Array.

2.3 Gib Ergebnisse aus einer Funktion zurück

Ein Rückgabewert ist die Ausgabe, die anderer Code von einem Aufruf erhält. Eine Ausgabe auf dem Bildschirm kann denselben Wert anzeigen, liefert dem aufrufenden Code aber ein anderes Ergebnis, sofern die Funktion ihn nicht auch zurückgibt.

Verwende den Aufruf als Wert
function double(n) {
  return n * 2;
}
const x = double(5) + 1;

double(5) gibt 10 zurück, sodass die Zuweisung 10 plus 1 berechnet und 11 speichert. Ein Aufruf kann überall dort stehen, wo ein passender Wert benötigt wird. double(double(3)) erhält zuerst 6 vom inneren Aufruf und gibt dann 12 vom äußeren zurück. Math.round(double(2.3)) verdoppelt auf 4,6 und rundet auf 5.

return beendet auch den aktuellen Aufruf sofort. Eine Wächterklausel (Guard Clause) behandelt einen Sonderfall vor der Hauptarbeit:

function safeDivide(a, b) {
  if (b === 0) {
    return null;
  }
  return a / b;
}

safeDivide(6, 0) gibt null zurück und erreicht die Division nie. safeDivide(6, 2) überspringt die Wächterklausel und gibt 3 zurück. Setze die Bedingung vor die Operation, die sie absichert.

Unterscheide Rückgabe und Anzeige
function addLog(a, b) {
  console.log(a + b);
}

addLog(2, 3) gibt 5 aus, aber sein Rückgabewert ist undefined. Daher gibt addLog(2, 3) * 2 den Wert 5 aus und wird zu NaN ausgewertet. Eine Funktion, die ihr Ende ohne Rückgabe erreicht oder ein alleinstehendes return; ausführt, gibt undefined zurück. Die Konsolenanzeige ist ein Seiteneffekt, nicht das nutzbare numerische Ergebnis des Aufrufs.

Wenn der aufrufende Code eine Summe benötigt, schreibe return a + b;. Der aufrufende Code kann dann entscheiden, ob er sie ausgibt, speichert oder in einer weiteren Berechnung verwendet. So bleibt die Entscheidung über die Anzeige außerhalb einer wiederverwendbaren Berechnung.

Halte den zurückgegebenen Ausdruck in derselben Zeile
function brokenAdd(a, b) {
  return
  a + b;
}

JavaScript beendet die return-Anweisung am Zeilenumbruch. Die Funktion gibt undefined zurück; der nachfolgende Ausdruck wird nie erreicht. Beginne bei einem langen Ausdruck diesen oder öffne eine Klammer in der return-Zeile und fahre dann darunter fort.

Fasse mehrere Ergebnisse in einem Wert zusammen

Ein Aufruf gibt einen Wert zurück, aber dieser Wert kann ein Array sein:

function minMax(nums) {
  return [Math.min(...nums), Math.max(...nums)];
}
const [low, high] = minMax([4, 9, 1]);

Das zurückgegebene Array ist [1, 9]. Die Destrukturierung weist seinen ersten Eintrag low und seinen zweiten high zu. Es bleibt ein Rückgabewert, der zwei nützliche Ergebnisse enthält.

Eine reine Funktion berechnet ihre Ausgabe aus ihren Argumenten und ändert außerhalb ihrer selbst nichts. double ist rein. Ausgaben, das Aktualisieren einer äußeren Variablen und das Verändern eines Eingabearrays sind Seiteneffekte. Reine Berechnungen lassen sich leicht mit Eingabe-Ausgabe-Beispielen prüfen; Seiteneffekte gehören in klar benannte Funktionen, wo das Programm sichtbare Änderungen benötigt.

Wichtige Erkenntnisse

  • return liefert den Wert des Aufrufs und beendet den aktuellen Aufruf.
  • Eine bloße Ausgabe belässt den Rückgabewert der Funktion bei undefined.
  • Beginne den zurückgegebenen Ausdruck in der return-Zeile.
  • Ein zurückgegebenes Array kann mehrere Ergebnisse enthalten.
  • Reine Funktionen hängen von ihren Argumenten ab und nehmen keine Änderungen außerhalb vor.

2.4 Verfolge Funktionsaufrufe im Aufrufstapel

Wenn eine Funktion eine andere aufruft, pausiert die aufrufende Funktion, bis dieser Aufruf zurückkehrt. Der Aufrufstapel hält die unerledigte Arbeit fest, damit die Ausführung an der richtigen Stelle fortgesetzt werden kann.

Füge Stapelrahmen hinzu und entferne sie

Jeder aktive Aufruf hat einen Stapelrahmen (Stack Frame), der seine Parameter, lokalen Variablen und die Stelle für die Rückkehr enthält. Ein neuer Aufruf kommt oben auf den Stapel; eine Rückkehr entfernt diesen obersten Rahmen.

function square(n) {
  return n * n;
}
function sumOfSquares(a, b) {
  return square(a) + square(b);
}
sumOfSquares(3, 4);

Zuerst beginnt sumOfSquares mit a gleich 3 und b gleich 4. Die Funktion ruft square(3) auf, das 9 zurückgibt und endet. Dann wird square(4) ausgeführt und gibt 16 zurück. Die pausierte Addition kann nun als 9 plus 16 abgeschlossen werden und sumOfSquares gibt 25 zurück. Die beiden Aufrufe von square überschneiden sich in diesem Ablauf nicht.

Jeder Aufruf erhält neue Parameter und lokale Variablen. Zwei Aufrufe derselben Funktion verwenden den lokalen numerischen Zustand eines früheren Aufrufs nicht erneut. Selbst wenn eine Funktion sich selbst aufruft, hat der neue Aufruf seinen eigenen Rahmen.

Verkleinere ein Problem mit Rekursion

Eine rekursive Funktion ruft sich selbst auf. Ein Basisfall endet ohne weiteren Selbstaufruf; ein rekursiver Fall macht einen Schritt auf diesen Basisfall zu.

function factorial(n) {
  if (n <= 1) {
    return 1;
  }
  return n * factorial(n - 1);
}

Für nicht negative ganzzahlige Eingaben multipliziert dies die Ganzzahlen abwärts bis 1. Verfolge factorial(4) in zwei Richtungen:

  1. factorial(4) wartet auf factorial(3).
  2. Dieser Aufruf wartet auf factorial(2).
  3. Dieser Aufruf wartet auf factorial(1).
  4. Der Basisfall gibt 1 zurück.
  5. Die wartenden Aufrufe geben 2, dann 6 und dann 24 zurück.

Die Multiplikation im Aufruf mit n gleich 4 erfolgt, nachdem der kleinere Aufruf zurückgekehrt ist. Sein eigenes n ist während des Wartens 4 geblieben.

Ein Basisfall muss erreichbar sein. Nur n === 1 zu verwenden, schlägt bei einer Starteingabe von 0 fehl, wenn jeder rekursive Aufruf 1 abzieht: Die Werte werden negativ und erreichen 1 nie. n <= 1 fängt sowohl 0 als auch 1 ab. Ohne einen Pfad zum Anhalten erschöpfen die wiederholten Aufrufe den Stapel. Chrome und Node.js melden „RangeError: Maximum call stack size exceeded“; Firefox verwendet „InternalError: too much recursion“.

Erkenne eine kleinere Kopie derselben Aufgabe
function countItems(list) {
  let count = 0;
  for (const item of list) {
    if (Array.isArray(item)) {
      count += countItems(item);
    } else {
      count += 1;
    }
  }
  return count;
}

countItems([1, [2, 3], [4, [5]]]) gibt 5 zurück. Jedes verschachtelte Array stellt dieselbe Zählfrage für eine kleinere Liste. Für eine einfache Fakultätsberechnung eignet sich auch eine Schleife; Rekursion ist besonders nützlich, wenn die Daten selbst verschachtelt sind.

Schlägt ein Aufruf fehl, lies den Stacktrace vom letzten Aufruf ganz oben aus. Die folgenden Einträge zeigen, welche noch nicht abgeschlossenen Aufrufe dorthin geführt haben. Wiederholte Einträge für eine Funktion können eine Rekursion zeigen, die nicht angehalten hat.

Wichtige Erkenntnisse

  • Der Aufrufstapel verfolgt nicht abgeschlossene Aufrufe; Rückgaben entfernen den jeweils letzten Rahmen.
  • Jeder Aufruf hat eigene Parameter und lokale Variablen.
  • Rekursion braucht einen Fall zum Anhalten und Schritte auf diesen Fall zu.
  • Rekursive Ergebnisse werden in umgekehrter Reihenfolge durch die wartenden Aufrufe zurückgegeben.
  • Ein Stacktrace zeigt den fehlgeschlagenen Aufruf und die Aufrufe, die dazu geführt haben.
  • Verschachtelte Daten können kleinere Instanzen derselben Aufgabe enthalten.
3

Erkläre, wo du eine Variable verwenden kannst

Variablennamen haben innerhalb von Gültigkeitsbereichen eine Bedeutung. Block- und Funktionsgrenzen sowie die Stelle, an der eine Funktion definiert ist, bestimmen, auf welche Deklarationen sie zugreifen kann. Du wirst diese Grenzen verfolgen, um einen Wert oder Fehler vorherzusagen, und dann Verdeckung von der Aktualisierung einer äußeren Variablen unterscheiden. Hoisting und die zeitliche Totzone erklären, warum ein Name bereits registriert sein kann, bevor die Ausführung seine Zeile erreicht, und dennoch nicht verwendbar ist.

3.1 Unterscheide Blockgültigkeitsbereich, Funktionsgültigkeitsbereich und globalen Gültigkeitsbereich

Der Gültigkeitsbereich einer Variablen ist der Bereich, in dem ihr Name verwendet werden kann. Gültigkeitsbereiche ermöglichen es Funktionen, ihre Arbeitsdaten getrennt zu halten und zugleich Eingaben und Werte aus dem umgebenden Code zu verwenden.

Ziehe Grenzen um Deklarationen

let und const haben einen Blockgültigkeitsbereich. Ein Block ist ein Bereich innerhalb geschweifter Klammern, etwa ein if-Rumpf, ein Schleifenrumpf oder ein Funktionsrumpf:

if (true) {
  const msg = "hi";
  console.log(msg);
}

Die Ausgabe funktioniert innerhalb des Blocks. Fügst du console.log(msg); danach hinzu, löst dies einen ReferenceError aus, weil dieser äußere Code nicht auf die Deklaration zugreifen kann. Eine andere äußere Deklaration mit demselben Namen wäre eine andere Variable.

Parameter und Variablen innerhalb einer Funktion sind lokal für diese Funktion. In function f() { let temp = 1; } kann Code außerhalb temp nicht lesen. Zwei Funktionen können jeweils ein lokales total haben, ohne es zu teilen. Jeder Aufruf beginnt mit neuen lokalen Variablen; eine zurückgegebene verschachtelte Funktion kann den Zugriff später über eine Closure erhalten.

Lies ältere var-Deklarationen
DeklarationGrenze
let oder const innerhalb eines BlocksDieser Block
var innerhalb einer FunktionDie umschließende Funktion, auch wenn die Deklaration in einem inneren Block steht
Deklaration auf oberster Ebene in einem gewöhnlichen SkriptDer globale Gültigkeitsbereich des Skripts
Deklaration auf oberster Ebene in einem ModulDieses Modul
function test() {
  if (true) {
    var x = 1;
  }
  return x;
}

test() gibt 1 zurück, weil var die Grenze des if-Blocks ignoriert. Ersetzt du es durch let x = 1;, schlägt die Rückgabe fehl: Das x des Blocks ist dort nicht verfügbar. Ebenso hält for (let i = 0; i < 3; i++) {} das i innerhalb der Schleife, während ein var i nach der Schleife in seiner umschließenden Funktion oder seinem Skript verfügbar bleibt und 3 enthält.

Suche von der geschriebenen Position der Funktion aus nach außen

Gültigkeitsbereiche sind verschachtelt. Eine innere Funktion kann Namen in ihren umschließenden Gültigkeitsbereichen verwenden, aber äußerer Code kann nicht nach innen in die lokalen Variablen einer inneren Funktion suchen. Das ist der lexikalische Gültigkeitsbereich: Die Stelle, an der der Code geschrieben steht, bestimmt die verfügbaren Namen.

const level = "global";
function show() {
  return level;
}
function run() {
  const level = "local";
  return show();
}

run() gibt „global“ zurück. show wurde außerhalb von run definiert, sodass sein umschließender Gültigkeitsbereich das äußere level enthält. Der Aufruf innerhalb von run gibt der Funktion keinen Zugriff auf das lokale level des aufrufenden Codes.

Setze gemeinsam genutzten Zustand bewusst ein

Eine Variable auf oberster Ebene in einem gewöhnlichen Skript ist global und für Funktionen innerhalb dieses Skripts erreichbar. Gemeinsam genutzte globale Variablen können dazu führen, dass unabhängige Aufgaben einander beeinflussen; auch Skripte auf derselben Seite können wegen gleicher Namen in Konflikt geraten. Die Übergabe von Eingaben und die Rückgabe von Ausgaben machen Abhängigkeiten sichtbar.

Deklariere jede Variable. Im strikten Modus löst count = 5; einen ReferenceError aus, wenn keine erreichbare Deklaration existiert. Ohne strikten Modus kann eine solche Zuweisung versehentlich eine globale Variable erzeugen. Ein Skript kann den strikten Modus mit "use strict"; an seinem Anfang aktivieren; Module aktivieren ihn automatisch. Bevorzuge const für Werte, denen kein neuer Wert zugewiesen wird, und let, wenn eine Neuzuweisung nötig ist.

Wichtige Erkenntnisse

  • let und const beachten Blockgrenzen; var beachtet die Grenze der umschließenden Funktion.
  • Funktionsparameter und lokale Variablen sind für Code außerhalb nicht verfügbar.
  • Der lexikalische Gültigkeitsbereich richtet sich danach, wo eine Funktion definiert ist, nicht danach, wo sie aufgerufen wird.
  • Variablen auf oberster Ebene eines Moduls gehören zum Modul.
  • Deklariere Namen ausdrücklich, um versehentliche globale Zuweisungen zu verhindern.

3.2 Bestimme, auf welche Variable ein Name verweist

Derselbe Name kann in mehreren Gültigkeitsbereichen vorkommen. JavaScript findet die passende Deklaration, indem es vom aktuellen Code aus nach außen sucht und beim ersten Treffer anhält.

Verfolge die Gültigkeitsbereichskette
let x = 1;
function f() {
  let x = 2;
  return x;
}

f() gibt 2 zurück. Die lokale Deklaration verdeckt das äußere x, sodass die Suche innerhalb von f endet. Der äußere Wert bleibt 1. Ein Parameter kann einen Namen auf dieselbe Weise verdecken. Deklariert kein Bereich der Kette einen Namen, löst sein Lesen einen ReferenceError aus.

Eine Zuweisung deklariert keine neue Variable. Bei let count = 0; function add() { count = count + 1; } findet add die äußere Deklaration und ändert sie. Das Einfügen von let ändert die Bedeutung: Es erzeugt einen neuen lokalen Namen, der genau den Wert verdecken kann, den du lesen wolltest.

Unterscheide Registrierung und Initialisierung

Hoisting beschreibt, wie Deklarationen registriert werden, bevor ihr Gültigkeitsbereich ausgeführt wird. Ihr Verhalten zu diesem frühen Zeitpunkt unterscheidet sich:

DeklarationVor ihrer Initialisierungszeile
FunktionsdeklarationIhre Funktion ist verfügbar
varIhr Wert ist undefined
let oder constEin Zugriff löst einen ReferenceError aus

Beispielsweise gibt console.log(a); var a = 5; den Wert undefined aus. Die Zuweisung von 5 hat noch nicht stattgefunden. console.log(b); let b = 5; schlägt dagegen fehl. Der Zeitraum, in dem der lexikalische Name existiert, aber nicht darauf zugegriffen werden kann, heißt zeitliche Totzone, oft mit TDZ abgekürzt.

Verfolge eine Verdeckung vor der Deklarationszeile
let n = 10;
function f() {
  console.log(n);
  let n = 20;
}
f();

Das innere n verdeckt das äußere im gesamten Funktionsrumpf, auch vor der geschriebenen Deklarationszeile. Bei console.log(n) befindet sich dieser innere Name noch in seiner TDZ, sodass der Aufruf einen ReferenceError auslöst. JavaScript überspringt eine nicht initialisierte lokale Deklaration nicht, um auf den äußeren Wert 10 zurückzugreifen.

Dasselbe Problem tritt bei let count = count + 1; innerhalb von add auf. Die rechte Seite liest das neue lokale count, bevor es initialisiert wurde. Soll ein äußerer Zähler aktualisiert werden, verwende eine Zuweisung ohne neue Deklaration. Soll ein eigener Wert berechnet werden, gib ihm einen anderen Namen und lies den äußeren Zähler ausdrücklich.

Unterscheide Neuzuweisung und Mutation

let und const können denselben Namen nicht erneut im selben Gültigkeitsbereich deklarieren; dies führt zu einem SyntaxError. Ein innerer Gültigkeitsbereich darf einen eigenen Namen deklarieren; das ist Verdeckung. var erlaubt wiederholte Deklarationen in seinem Gültigkeitsbereich, wodurch eine versehentliche Wiederverwendung von Namen schwerer zu erkennen ist.

const verhindert eine Neuzuweisung an seine Variable. Es friert ein Array nicht ein:

const list = [];
list.push(1);

Die in list gespeicherte Referenz bleibt gleich, während sich das Array zu [1] ändert. list = []; würde versuchen, die gespeicherte Referenz zu ersetzen, und fehlschlagen.

Platziere eine Deklaration im kleinsten Gültigkeitsbereich, in dem sie benötigt wird. Verschiedene Namen für verschiedene Aufgaben verringern den Aufwand, Verdeckungen gedanklich zu verfolgen. Eine Initialisierung vor der Verwendung vermeidet Fehler durch die TDZ.

Wichtige Erkenntnisse

  • Die Namenssuche endet bei der nächstgelegenen umschließenden Deklaration.
  • Eine lokale Deklaration verdeckt einen äußeren Namen schon vor ihrer Initialisierung.
  • var beginnt mit undefined; let und const sind in ihrer TDZ nicht zugänglich.
  • Eine Zuweisung aktualisiert eine vorhandene Bindung; eine Deklaration erzeugt eine.
  • const verhindert eine Neuzuweisung, während sich der Inhalt eines Arrays weiterhin ändern kann.
  • Eine erneute Deklaration im selben Gültigkeitsbereich unterscheidet sich von einer Verdeckung in einem verschachtelten Bereich.
4

Gib Funktionen mit Closures ein eigenes Gedächtnis

Eine zurückgegebene Funktion kann Zugriff auf die Variablen um ihre Definition herum behalten. Diese erhaltene Verbindung gibt einem Zähler ein Gedächtnis und ermöglicht es einer Fabrik, Funktionen mit eigenen Einstellungen zu erstellen. Du wirst getrennte Fabrikaufrufe, gemeinsam genutzten Zustand und spätere Änderungen an eingefangenen Variablen verfolgen und anschließend mit denselben Regeln erklären, wie in einer Schleife erzeugte Funktionen arbeiten. Privater Zustand ergibt sich aus dem gewöhnlichen lexikalischen Gültigkeitsbereich, selbst nachdem der äußere Aufruf zurückgekehrt ist.

4.1 Erkläre, was eine Closure behält

Eine Funktion behält eine Verbindung zu dem Gültigkeitsbereich, in dem sie erzeugt wurde. Die Funktion und dieser erhaltene Zugriff bilden eine Closure. Die Verbindung ist besonders nützlich, wenn eine verschachtelte Funktion zurückgegeben und aufgerufen wird, nachdem ihr äußerer Aufruf beendet ist.

Gib einer zurückgegebenen Funktion ein Gedächtnis
function makeCounter() {
  let count = 0;
  return function () {
    count = count + 1;
    return count;
  };
}
const counter = makeCounter();

Der Aufruf von makeCounter erzeugt count, erzeugt eine innere Funktion und gibt diese Funktion zurück. Den inneren Rumpf führt er noch nicht aus. counter enthält nun den Funktionswert. Sein erster Aufruf ändert das erhaltene count von 0 auf 1 und gibt 1 zurück. Der nächste Aufruf ändert dieselbe Variable auf 2 und gibt 2 zurück.

Der Stapelrahmen des äußeren Aufrufs ist beendet, aber die eingefangene Variable bleibt zugänglich. Eine lokale Variable, die nicht mehr erreichbar verwendet werden kann, kann freigegeben werden; hier benötigt die aufrufbare innere Funktion sie noch. Der Zugriff über die Closure bleibt nach einer Rückkehr erhalten, ohne die Variable global zu machen.

Erzeuge mit getrennten Aufrufen unabhängige Speicher
const a = makeCounter();
const b = makeCounter();
a();
a();
b();

Die Ergebnisse sind 1, 2 und 1. Jeder Fabrikaufruf hat ein eigenes count erzeugt. Die beiden aus getrennten Aufrufen zurückgegebenen Funktionen haben denselben Code, aber unterschiedliche erhaltene Umgebungen.

Das Kopieren einer zurückgegebenen Funktionsreferenz funktioniert anders. Bei const alias = a; verwendet alias() den vorhandenen Zähler von a; es ruft nicht makeCounter auf und erzeugt keine weitere Umgebung.

Behalte Variablen, keine Momentaufnahmen
let greeting = "Hi";
const say = () => greeting;
greeting = "Hello";

say() gibt „Hello“ zurück. Die Closure liest die Variable bei ihrer Ausführung, sodass eine spätere Aktualisierung sichtbar ist. Sie speichert bei ihrer Erzeugung keine eingefrorene Kopie von „Hi“.

Code außerhalb kann nicht direkt auf das lokale count zugreifen und counter.count ist undefined. Nur Code mit Zugriff auf diese lexikalische Umgebung kann die Variable lesen oder aktualisieren. Der Aufruf des Zählers aus einer anderen Funktion ändert nicht, welche Zählervariable er verwendet.

Teile Zustand innerhalb eines äußeren Aufrufs
function makeWallet() {
  let balance = 0;
  return [
    function deposit(n) { balance += n; return balance; },
    function getBalance() { return balance; }
  ];
}
const [deposit, getBalance] = makeWallet();
deposit(50);
deposit(20);

getBalance() gibt 70 zurück, weil beide zurückgegebenen Funktionen im selben Aufruf erzeugt wurden und auf dasselbe balance verweisen. Ein weiterer Aufruf von makeWallet() erzeugt einen eigenen Kontostand.

Wichtige Erkenntnisse

  • Eine Closure behält Zugriff auf den Gültigkeitsbereich, in dem ihre Funktion erzeugt wurde.
  • Eine eingefangene Variable kann nach der Rückkehr ihres äußeren Aufrufs verwendbar bleiben.
  • Getrennte Fabrikaufrufe erzeugen getrennte Umgebungen.
  • Closures lesen aktuelle Variablenwerte, keine Momentaufnahmen vom Zeitpunkt ihrer Erzeugung.
  • Funktionen aus demselben äußeren Aufruf können privaten Zustand teilen.

4.2 Nutze Closures für Funktionsfabriken und privaten Zustand

Eine Funktionsfabrik nutzt den Zugriff über eine Closure, um Funktionen mit eigenen Einstellungen oder eigenem Zustand zu erzeugen. Der aufrufende Code übergibt die Konfiguration einmal und nutzt die zurückgegebene Funktion danach vielfach.

Konfiguriere eine Funktion mit einem Parameter
function makeMultiplier(factor) {
  return (n) => n * factor;
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);

double(5) gibt 10 und triple(5) gibt 15 zurück. Jede zurückgegebene Pfeilfunktion behält den factor aus ihrem eigenen Fabrikaufruf. Die äußere Funktion konfiguriert das Verhalten; die innere führt die wiederholte Arbeit aus.

Die kompakte Form lautet const makeMultiplier = (factor) => (n) => n * factor;. Zwei Pfeile bedeuten, dass zwei Funktionen erzeugt werden, nicht zwei aufeinanderfolgende Berechnungen mit einer Zahl. makeMultiplier(4)(5) gibt zuerst eine mit 4 konfigurierte Funktion zurück und ruft sie dann mit 5 auf, um 20 zu erhalten.

Setze eine Regel für privaten Zustand durch
function makeLimiter(max) {
  let used = 0;
  return function () {
    if (used >= max) {
      return false;
    }
    used = used + 1;
    return true;
  };
}
const tryLogin = makeLimiter(3);

Die ersten drei Aufrufe geben true zurück und erhöhen used. Der vierte gibt false zurück, bevor der Wert erhöht wird. used ist lokal für den Fabrikaufruf, sodass Code außerhalb die Variable nicht über diesen Namen zurücksetzen kann. Die zurückgegebene Funktion steuert, welche Änderungen möglich sind. Ein weiterer Begrenzer hätte seine eigene Variable used und seinen eigenen Höchstwert.

Untersuche in Schleifen erzeugte Funktionen
const fns = [];
for (var i = 0; i < 3; i++) {
  fns.push(() => i);
}

Die Schleife erzeugt drei Funktionen, aber nur ein var i. Nach der Schleife enthält diese Variable 3. Der Aufruf einer beliebigen gespeicherten Funktion gibt nun 3 zurück, weil jede Closure dieselbe aktuelle Bindung liest. Das Erzeugen einer Funktion wertet ihren Rumpf nicht aus und erstellt keine Momentaufnahme des aktuellen i.

Änderst du den Schleifenkopf zu let i = 0, erhält jeder Schleifendurchlauf eine eigene Bindung. Die gespeicherten Funktionen geben dann 0, 1 und 2 zurück. Derselbe Unterschied erklärt eine verzögerte Ausgabe: Ein setTimeout-Callback, der nach der Schleife var i liest, sieht den Endwert, während die let-Version den Wert jedes Schleifendurchlaufs behält.

Erkenne einen sofort aufgerufenen Ausdruck

Älterer Code erzeugt oft einen privaten Funktionsgültigkeitsbereich und führt die Funktion sofort aus:

(function () {
  var hidden = 1;
  console.log(hidden);
})();

Dies ist ein sofort aufgerufener Funktionsausdruck, kurz IIFE. Die äußeren Klammern machen daraus einen Funktionsausdruck; die abschließenden Klammern rufen ihn auf. hidden gehört zu dieser Funktion und bleibt dadurch außerhalb des umgebenden globalen Gültigkeitsbereichs. Das Muster ist in Code verbreitet, der vor Deklarationen mit Blockgültigkeitsbereich und Modulen geschrieben wurde.

Der erhaltene Zugriff wirkt sich auch auf den Speicher aus. Eine aufrufbare Closure, die ein großes Array benötigt, hält diese Daten erreichbar. Sind die Funktion und ihre eingefangenen Daten nicht mehr erreichbar, können sie freigegeben werden. Speichere langlebige Closures bewusst, wenn ihr Speicher Teil der Aufgabe ist.

Wichtige Erkenntnisse

  • Fabriken geben Funktionen zurück, die durch ihre Argumente konfiguriert sind.
  • Private Variablen können über die zurückgegebenen Operationen geändert werden.
  • Closures einer var-Schleife teilen sich eine Bindung; Schleifendurchläufe mit let erhalten getrennte Bindungen.
  • Eine IIFE erzeugt einen Funktionsgültigkeitsbereich und führt die Funktion sofort aus.
  • Erreichbare Closures halten die eingefangenen Daten erreichbar, die sie benötigen.
5

Übergib Funktionen an andere Funktionen

Durch die Übergabe einer Funktion kann der aufrufende Code einen Teil des Verhaltens einer anderen Funktion bestimmen. Du wirst untersuchen, wie empfangende Funktionen Callbacks aufrufen, welche Argumente sie übergeben und wie sie das Ergebnis verwenden. Bekannte Arraymethoden werden dann zu Beispielen für wiederverwendbare Muster mit Funktionen höherer Ordnung. Schließlich werden zurückgegebene Funktionen Aufrufe umhüllen, Ausgaben mit Eingaben verbinden oder ausgewählte Argumente für die spätere Verwendung bereithalten und dabei Funktionswerte mit dem Zugriff über Closures verbinden.

5.1 Übergib einer Funktion einen Callback

Ein Callback ist eine Funktion, die als Argument übergeben wird, damit eine andere Funktion sie aufruft. So kann die empfangende Funktion die Gesamtaufgabe bearbeiten, während der aufrufende Code eine bestimmte Aktion oder Prüfung liefert.

Übergib eine Aktion an eine Schleife
function repeat(times, action) {
  for (let i = 0; i < times; i++) {
    action(i);
  }
}
repeat(3, (i) => console.log(i));

repeat enthält die Schleife und wählt die Werte 0, 1 und 2. Die Pfeilfunktion bestimmt, was mit jedem Wert geschieht: Er wird ausgegeben. Jedes action(i) ruft die übergebene Funktion mit der Nummer des jeweiligen Schleifendurchlaufs auf.

Du kannst eine vorhandene Funktion übergeben oder eine direkt an Ort und Stelle schreiben. repeat(2, sayHi) übergibt die benannte Funktion. repeat(2, (i) => console.log("Round " + (i + 1))) gibt „Round 1“ und „Round 2“ aus. Ein Callback darf Argumente ignorieren, die er nicht benötigt.

Übergib den Wert und rufe die Funktion nicht vorzeitig auf

repeat(3, sayHi()) wertet sayHi() beim Vorbereiten der Argumente aus. Wenn sayHi nur etwas ausgibt, ist sein Rückgabewert undefined, sodass repeat diesen Wert als action erhält. Das spätere action(i) schlägt mit einem TypeError fehl, weil undefined nicht aufrufbar ist.

Verwende repeat(3, sayHi), um die Funktion zu übergeben. Wenn der spätere Aufruf ausgewählte Argumente benötigt, übergib einen Wrapper wie () => sayHi(), damit der Aufruf innerhalb des Callback-Rumpfs stattfindet. Das Erzeugen und das Aufrufen einer Funktion sind getrennte Schritte.

Prüfe, welche Argumente die empfangende Funktion liefert

Array-Callbacks erhalten oft mehr als ein Argument: Element, Index und das gesamte Array. Die Übergabe einer vorhandenen Funktion ist nur dann sicher, wenn diese Eingaben zur Bedeutung ihrer Parameter passen.

["1", "2", "3"].map(parseInt);

Dies gibt [1, NaN, NaN] zurück. parseInt akzeptiert ein zweites Argument für die Zahlenbasis, sodass der Arrayindex sein Verhalten ändert. Verwende ["1", "2", "3"].map((s) => parseInt(s, 10)), um die Basis 10 ausdrücklich zu wählen, oder .map(Number), um diese Zeichenketten umzuwandeln.

Gib eine Antwort an die empfangende Funktion zurück
function countIf(arr, test) {
  let n = 0;
  for (const x of arr) {
    if (test(x)) {
      n++;
    }
  }
  return n;
}

countIf([3, 8, 12, 5], (x) => x > 4) gibt 3 zurück. Der Callback beantwortet für jedes Element eine Frage; countIf nutzt jede Antwort, um zu entscheiden, ob erhöht wird. Vergleiche repeat, dessen Schleife den Rückgabewert des Callbacks ignoriert. Der Code oder die Dokumentation der empfangenden Funktion nennt dir sowohl die Argumente als auch die Rolle des Ergebnisses.

Callbacks können sofort oder später ausgeführt werden. repeat und Arraymethoden rufen sie synchron auf, bevor sie zurückkehren. setTimeout(sayHi, 2000) plant einen Callback für später ein, während der nachfolgende Code sofort weiterläuft. Die Bezeichnung Callback beschreibt, wie eine Funktion übergeben wird, nicht wann sie ausgeführt werden muss.

Wichtige Erkenntnisse

  • Ein Callback ist eine Funktion, die zum Aufruf durch eine andere Funktion übergeben wird.
  • Die empfangende Funktion bestimmt die Argumente des Callbacks und ob sein Ergebnis verwendet wird.
  • Zu früh gesetzte Aufrufklammern können ein Ergebnis statt einer Funktion übergeben.
  • Argumente von Array-Callbacks können das Verhalten einer vorhandenen Funktion ändern.
  • Callbacks können synchron oder verzögert ausgeführt werden.

5.2 Nutze und schreibe Funktionen höherer Ordnung

Eine Funktion höherer Ordnung nimmt eine Funktion entgegen, gibt eine Funktion zurück oder tut beides. Der Funktionswert liefert Verhalten, während die Funktion höherer Ordnung ein wiederverwendbares Muster verwaltet.

Schreibe das Muster einmal
function myMap(arr, transform) {
  const result = [];
  for (const item of arr) {
    result.push(transform(item));
  }
  return result;
}

myMap([1, 2, 3], (n) => n * n) gibt [1, 4, 9] zurück. Die Schleife und das Erstellen des Ausgabearrays bleiben gleich; der Callback bestimmt, wie sich jeder Eintrag ändert. Ein entsprechender Filter fügt ein Element nur hinzu, wenn test(item) einen Truthy-Wert ergibt. Mit [4, 7, 10, 3] und (n) => n % 2 === 0 behält dieses Muster [4, 10].

Wähle den benötigten Vertrag für den Callback
MethodeAufgabe des CallbacksErgebnis der Methode
mapJeden transformierten Wert zurückgebenEin neues Array gleicher Länge
filterFür zu behaltende Elemente einen Truthy-Wert zurückgebenEin neues Array ausgewählter Elemente
findAuf ein passendes Element prüfenErster Treffer oder undefined
forEachFür jedes Element eine Aktion ausführenundefined
reduceDen nächsten Akkumulator zurückgebenDer endgültige Akkumulator

map, filter und find verändern das Eingabearray selbst nicht, aber Callbacks können es dennoch verändern. Dass eine Methode ein neues Array zurückgibt, macht nicht jeden übergebenen Callback rein.

Führe ein Ergebnis durch eine Reduktion
[5, 10, 15].reduce((total, n) => total + n, 0);

Der anfängliche Akkumulator ist 0. Der Callback gibt für das erste Element 5, für das zweite 15 und für das dritte 30 zurück. Jeder Rückgabewert wird zum total des nächsten Aufrufs. Nach dem letzten Element gibt die Methode 30 zurück. Bei einem leeren Array ohne anfänglichen Akkumulator löst reduce einen TypeError aus; ein ausdrücklicher Anfangswert legt auch das nutzbare Ergebnis für eine leere Eingabe fest.

Da Methoden Arrays zurückgeben, lassen sich Aufrufe verketten. [3, 8, 12, 5].filter((n) => n > 4).map((n) => n * 2) behält zuerst [8, 12, 5] und transformiert es dann zu [16, 24, 10]. Lies die Kette in Ausführungsreihenfolge von links nach rechts.

Gib eine numerische Sortierregel an

Ohne Vergleichsfunktion vergleicht sort Einträge als Zeichenketten. [10, 9, 1].sort() ergibt daher [1, 10, 9]. Verwende für eine aufsteigende numerische Reihenfolge .sort((a, b) => a - b). Ein negativer Rückgabewert setzt a an die erste Stelle, ein positiver b, und bei null gelten die beiden für die Reihenfolge als gleichwertig. Anders als map und filter ordnet sort sein Eingabearray um.

Prüfe die Rückgabewerte

[1, 2, 3].map((n) => { n * 2; }) ergibt [undefined, undefined, undefined], weil der Block keine Rückgabe hat. Auch der Versuch, mit forEach durch Zuweisung ein neues Ergebnis zu erstellen, schlägt fehl, wenn du erwartest, dass die Methode selbst ein Array zurückgibt. Wähle map für Transformationen und forEach für Aktionen, deren gesammelte Ausgaben nicht benötigt werden.

Eine Methode höherer Ordnung drückt ein bekanntes Muster klar aus. Eine einfache Schleife bleibt nützlich, wenn die Arbeit break benötigt oder nicht zum Vertrag der Methode passt.

Wichtige Erkenntnisse

  • Funktionen höherer Ordnung nehmen Funktionen entgegen, geben Funktionen zurück oder tun beides.
  • map, filter und find verwenden Callback-Ergebnisse auf unterschiedliche Weise.
  • forEach gibt undefined zurück; reduce gibt den endgültigen Akkumulator zurück.
  • Ein Reduktionscallback muss den nächsten Akkumulator zurückgeben.
  • Numerisches Sortieren benötigt eine Vergleichsfunktion und verändert das ursprüngliche Array.
  • Eine Methode, die ein neues Array zurückgibt, kann dennoch Callbacks mit Seiteneffekten ausführen.

5.3 Erstelle neue Funktionen aus vorhandenen Funktionen

Eine Funktion kann eine neue Funktion zurückgeben, die sich eine vorhandene merkt. Wrapper, Funktionskomposition und partielle Anwendung nutzen dieses Muster, um Verhalten anzupassen, ohne den Rumpf der ursprünglichen Funktion zu kopieren.

Umhülle einen Aufruf und erhalte sein Ergebnis
function logged(fn) {
  return (...args) => {
    console.log("Calling with", args);
    const result = fn(...args);
    console.log("Result", result);
    return result;
  };
}
const add = (a, b) => a + b;

logged(add) erzeugt eine Funktion, die add als fn behält. Der Aufruf logged(add)(2, 3) sammelt die Argumente, leitet sie an add weiter, gibt den Rückgabewert aus und liefert 5 zurück. Rest sammelt die Eingaben des Wrappers; Spread leitet sie weiter. Die abschließende Rückgabe hält die Ausgabe für spätere Berechnungen verfügbar.

logged(add(2, 3)) übergibt stattdessen die Zahl 5. Wird dieser Wrapper später aufgerufen, versucht er, 5 aufzurufen, und löst einen TypeError aus. Es gibt zwei Schritte: Konfiguriere den Wrapper mit einer Funktion und rufe dann die zurückgegebene Funktion mit Eingaben auf.

Behalte Zustand für eine Operation
function once(fn) {
  let done = false;
  let result;
  return (...args) => {
    if (!done) {
      done = true;
      result = fn(...args);
    }
    return result;
  };
}

Bei einer Operation, die normal zurückkehrt, führt der erste Aufruf fn aus und speichert das Ergebnis. Spätere Aufrufe geben diesen gespeicherten Wert zurück, ohne fn erneut auszuführen, selbst wenn ihre Argumente verschieden sind. Der boolesche Wert hält fest, ob die Operation ausgeführt wurde; den Wahrheitswert des Ergebnisses zu prüfen, wäre falsch, wenn ein zulässiges Ergebnis 0 oder false ist. Jeder Aufruf von once erzeugt ein eigenes Flag und ein eigenes gespeichertes Ergebnis.

Übergib ein Ergebnis an eine andere Funktion
const compose = (f, g) => (x) => f(g(x));
const addOne = (n) => n + 1;
const square = (n) => n * n;

compose(square, addOne)(3) führt zuerst addOne(3) aus und quadriert dann 4, um 16 zurückzugeben. compose(addOne, square)(3) quadriert zuerst 3 und addiert dann 1; das Ergebnis ist 10. Die Funktion, die der Eingabe am nächsten steht, wird zuerst ausgeführt, sodass die Reihenfolge der Argumente von compose wichtig ist.

Lege einige Argumente im Voraus fest
function partial(fn, ...fixed) {
  return (...rest) => fn(...fixed, ...rest);
}
const greetWith = (greeting, name) => greeting + ", " + name;
const hello = partial(greetWith, "Hello");

hello("Ana") ruft greetWith("Hello", "Ana") auf und gibt „Hello, Ana“ zurück. Diese Hilfsfunktion für partielle Anwendung setzt feste Argumente an den Anfang und spätere Argumente dahinter; eine Änderung dieser Reihenfolge ändert den zugrunde liegenden Aufruf.

Wichtige Erkenntnisse

  • Ein Wrapper behält eine Funktion und ergänzt deren Aufruf um ein Verhalten.
  • Rest und Spread ermöglichen einem Wrapper, einzelne Argumente weiterzuleiten.
  • Die Rückgabe des Ergebnisses der umhüllten Funktion hält es für den aufrufenden Code nutzbar.
  • Ein Once-Wrapper behält sowohl ein Flag als auch das erste Ergebnis.
  • compose(f, g) wendet g vor f an.
  • Partielle Anwendung legt ausgewählte Argumente vor späteren Aufrufen fest.

Funktionen und Closures in JavaScript: Beispiele

5 Interaktive Lektionen

1

Erstelle, speichere und rufe eine Funktion auf

11 Schritte
Verfolge eine wiederverwendbare Begrüßung durch Deklarationen, Funktionswerte und Pfeilfunktionsrümpfe.
  1. 1
    EinführungTrenne das Rezept vom Aufruf

    Eine Funktion speichert Anweisungen, sodass mehrere Aufrufe sie wiederverwenden können. Ihre Definition beschreibt eine Aufgabe; ein Aufruf führt diese Aufgabe mit bestimmten Eingaben aus.

    Verfolge eine Begrüßung durch drei Formen: eine Deklaration, einen in einer Variablen gespeicherten Ausdruck und eine Pfeilfunktion. Achte bei den Änderungen der Syntax auf zwei Einzelheiten: wann die Funktion verfügbar wird und ob ihr Rumpf einen Wert zurückgibt.

  2. 2
    BeispielDefiniere eine Begrüßung
    function greet(name) {
      console.log("Hello, " + name + "!");
    }
    

    Das Schlüsselwort, der Name, die Parameterliste und der Rumpf bilden eine Deklaration. Bisher wurde nichts ausgegeben: Die Definition speichert die Anweisungen.

    name ist ein Parameter, dessen Wert von einem Aufruf stammt. Der Rumpf lässt sich wiederverwenden, weil er beschreibt, wie die Begrüßung aufgebaut wird, ohne den Namen einer Person im Voraus festzulegen.

  3. 3
    BeispielFühre die gespeicherten Anweisungen aus
    greet("Amira");
    greet("Li");
    

    Die Aufrufe geben „Hello, Amira!“ und „Hello, Li!“ in getrennten Zeilen aus. Jeder beginnt denselben Rumpf erneut mit seiner eigenen Eingabe.

    Der Ausdruck greet allein ist der Funktionswert. Die Klammern in greet("Li") bewirken die Ausführung. Auch eine Funktion ohne Eingaben braucht Aufrufklammern, wie in sayHi().

  4. 4
    HerausforderungSage das Ergebnis des frühen Aufrufs voraus

    Was wird ausgegeben, wenn dieses Skript läuft?

    show();
    function show() { console.log("Ready"); }
    

    ---reveal---

    Es gibt einmal „Ready“ aus. Die Deklaration unterliegt innerhalb ihres Gültigkeitsbereichs dem Hoisting, sodass die Funktion in der ersten Zeile verfügbar ist. Hoisting führt den Rumpf nicht von selbst aus: Das tut der ausdrückliche Aufruf show(). Eine Zeile, die nur show; enthält, würde die Funktion lesen, ohne etwas auszugeben.

  5. 5
    ErklärungBehalte die Funktion als Wert
    const square = function (n) {
      return n * n;
    };
    const alias = square;
    

    Der Ausdruck erzeugt eine Funktion und square speichert sie. Die zweite Zuweisung kopiert ihre Referenz in alias; sie berechnet kein Quadrat. Sowohl square(3) als auch alias(3) geben 9 zurück.

    Im Vergleich dazu ruft const answer = square(3); die Funktion auf und speichert die zurückgegebene Zahl.

  6. 6
    HerausforderungFinde die erste Fehlerstelle

    Gibt dieses Skript 16 aus?

    console.log(square(4));
    const square = function (n) { return n * n; };
    

    ---reveal---

    Nein. Das Lesen von square vor seiner Initialisierung mit const löst einen ReferenceError aus. Die Zuweisung wurde noch nicht ausgeführt, sodass der Funktionswert des Ausdrucks nicht verfügbar ist. Verschiebe den Aufruf unter die Zuweisung oder verwende eine Funktionsdeklaration, wenn die Funktion in diesem Gültigkeitsbereich früher aufrufbar sein muss.

  7. 7
    BeispielVerwende einen Ausdrucksrumpf
    const square = (n) => n * n;
    const add = (a, b) => a + b;
    

    Der Ausdrucksrumpf einer Pfeilfunktion gibt seinen Ausdruck zurück, sodass square(4) den Wert 16 und add(2, 3) den Wert 5 zurückgibt.

    Bei einem einfachen Parameter dürfen die Klammern entfallen: n => n * n. Leere Listen, mehrere Parameter sowie Standard- und Restparameter brauchen sie. Pfeilfunktionen sind Ausdrücke, sodass diese const-Variablen vor der Verwendung weiterhin initialisiert werden müssen.

  8. 8
    HerausforderungBehebe ein fehlendes Ergebnis

    Warum gibt add(2, 3) den Wert undefined zurück und welche Änderung führt dazu, dass 5 zurückgegeben wird?

    const add = (a, b) => { a + b; };
    

    ---reveal---

    Die geschweiften Klammern beginnen einen Block, sodass der Ausdruck ausgewertet, aber nicht zurückgegeben wird. Schreibe (a, b) => { return a + b; } oder entferne die geschweiften Klammern und verwende (a, b) => a + b. Beide Korrekturen geben die Summe zurück. Ein bloßes Semikolon nach der Zuweisung ändert das Rückgabeverhalten des Rumpfs nicht.

  9. 9
    TippGib einen Objektausdruck zurück
    const makePoint = (x, y) => ({ x: x, y: y });
    

    Die runden Klammern machen das Objektliteral zu einem Ausdruck, der vom Ausdrucksrumpf zurückgegeben wird. Eine öffnende geschweifte Klammer ohne umgebende runde Klammern würde stattdessen einen Block beginnen.

    Pfeilfunktionen haben kein eigenes this oder arguments und können keine Konstruktoren sein. Die kurze Form eignet sich gut für einfache Berechnungen, aber diese Unterschiede sind wichtig, wenn andere Arten von Funktionen umgeschrieben werden.

  10. 10
    HerausforderungVerfolge eine Funktion und ihren weiteren Namen

    Was gibt copy(5) zurück?

    const adjust = (n) => { return n + 2; };
    const copy = adjust;
    

    ---reveal---

    Der Aufruf gibt 7 zurück. Der Block der Pfeilfunktion enthält eine ausdrückliche Rückgabe und copy enthält dieselbe aufrufbare Funktion wie adjust. Die Zuweisung hat die Funktion nicht aufgerufen und kein numerisches Ergebnis gespeichert. Würdest du const copy = adjust; durch const copy = adjust(5); ersetzen, würde 7 gespeichert, was nicht aufrufbar ist.

  11. 11
    ZusammenfassungLies die Operation in jeder Zeile

    Eine Deklaration erzeugt eine benannte Aufgabe, die früh in ihrem Gültigkeitsbereich verfügbar ist. Ein Ausdruck erzeugt einen Funktionswert, wenn die Ausführung ihn erreicht. Der Ausdrucksrumpf einer Pfeilfunktion gibt automatisch zurück; ihr Blockrumpf braucht return.

    Unterscheide beim Verfolgen von Code zwischen einer Funktionsdefinition, der Zuweisung einer Referenz und einem Aufruf. Das sind verschiedene Operationen, auch wenn sie denselben Namen verwenden.

2

Verfolge Eingaben, Ausgaben und wartende Aufrufe

16 Schritte
Ordne Argumente zu, verfolge Rückgabewerte und arbeite eine kurze rekursive Berechnung rückwärts ab.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungVerfolge, was in einen Aufruf hinein- und herausgeht
  2. 2
    BeispielOrdne Argumente nach ihrer Position zu
  3. 3
    BeispielUnterscheide Mutation und Neuzuweisung
  4. 4
    HerausforderungErhalte die ursprüngliche Eingabe
  5. 5
    ErklärungDer Standardwert gilt bei undefined
  6. 6
    BeispielSammle und entfalte anschließend
  7. 7
    HerausforderungVerwende einen Standardwert mit Rest
  8. 8
    BeispielGib die benötigte Zahl an den aufrufenden Code zurück
  9. 9
    BeispielBeende einen Ausführungszweig mit einer vorzeitigen Rückkehr
  10. 10
    HerausforderungWerte von innen nach außen aus
  11. 11
    BeispielBehalte den nicht abgeschlossenen Aufruf im Blick
  12. 12
    ErklärungGib der Rekursion einen Pfad zum Anhalten
  13. 13
    BeispielArbeite die wartenden Produkte rückwärts ab
  14. 14
    HerausforderungKorrigiere eine rekursive Abbruchbedingung
  15. 15
    VerbindungLies einen Fehler anhand des Stapels
  16. 16
    ZusammenfassungVerfolge einen Wert über jede Grenze
3

Finde die Deklaration hinter einem Namen

10 Schritte
Verfolge Blockgrenzen, die lexikalische Suche und den Zeitpunkt der Initialisierung.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungFrag dich, wohin der Name gehört
  2. 2
    BeispielVergleiche Block- und Funktionsgrenzen
  3. 3
    BeispielNutze die Stelle der Definition
  4. 4
    HerausforderungVerfolge einen lokalen und einen äußeren Wert
  5. 5
    ErklärungHalte bei der nächstgelegenen Deklaration an
  6. 6
    KernaussageRegistriert bedeutet nicht initialisiert
  7. 7
    HerausforderungSage das Lesen eines verdeckten Namens voraus
  8. 8
    TippUnterscheide Neuzuweisung und Mutation
  9. 9
    HerausforderungVerbinde einen Callback mit dem Zeitpunkt der Suche
  10. 10
    ZusammenfassungBestimme vor dem Wert die Stelle
4

Gib zurückgegebenen Funktionen ein eigenes Gedächtnis

11 Schritte
Verfolge gemeinsamen und unabhängigen Closure-Zustand und untersuche anschließend innerhalb einer Schleife erzeugte Funktionen.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungBehalte den Zugriff nach einer Rückkehr
  2. 2
    BeispielErstelle einen Zähler, ohne ihn aufzurufen
  3. 3
    BeispielAktualisiere die erhaltene Variable
  4. 4
    HerausforderungVergleiche unabhängige Zähler
  5. 5
    KernaussageBehalte die Bindung, keine Momentaufnahme
  6. 6
    BeispielErstelle eine konfigurierte Berechnung
  7. 7
    BeispielHalte einen Nutzungszähler privat
  8. 8
    HerausforderungErkläre drei gleiche Ergebnisse
  9. 9
    VerbindungErkenne einen privaten Gültigkeitsbereich in älterem Code
  10. 10
    HerausforderungTeile eine konfigurierte Operation
  11. 11
    ZusammenfassungVerfolge, wie viele Umgebungen existieren
5

Übergib Verhalten und erstelle angepasste Funktionen

15 Schritte
Verfolge Callback-Argumente, Ergebnisse von Arraymethoden und Funktionen, die konfigurierte Funktionen zurückgeben.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungLass eine Funktion das veränderliche Verhalten liefern
  2. 2
    BeispielRufe die übergebene Aktion auf
  3. 3
    HerausforderungVerwende ein Callback-Ergebnis
  4. 4
    TippUntersuche alle Callback-Argumente
  5. 5
    BeispielSchreibe ein Transformationsmuster
  6. 6
    ErklärungWähle die Form des Ergebnisses
  7. 7
    HerausforderungVerfolge eine Auswahl und eine Transformation
  8. 8
    KernaussageAchte bewusst auf numerische Reihenfolge
  9. 9
    BeispielUmhülle einen Aufruf und leite ihn weiter
  10. 10
    ErklärungMerke dir, ob ein Aufruf ausgeführt wurde
  11. 11
    BeispielSetze Funktionen von innen nach außen zusammen
  12. 12
    BeispielLege Eingaben vor dem späteren Aufruf fest
  13. 13
    HerausforderungÜbergib einen konfigurierten Callback
  14. 14
    HerausforderungBehebe ein verlorenes Wrapper-Ergebnis
  15. 15
    ZusammenfassungPrüfe beide Schritte bei Funktionen höherer Ordnung

Funktionen und Closures in JavaScript: Übungsaufgaben

50 Übungsfragen in 5 Sets

Set 1: Lies Funktionen und verfolge ihre Werte10 Übungsfragen

Frage 1 von 10

Gegeben ist function showLabel() { console.log("Ready"); }. Welche Anweisung ruft die Funktion auf?
  1. "showLabel";
  2. showLabel();
  3. showLabel;
  4. const label = showLabel;
Antwort zeigenAntwort ausblenden
Antwort: B
showLabel(); ruft die Funktion auf, sodass ihr Rumpf „Ready“ ausgibt. Das Lesen von showLabel ohne Klammern liefert nur den Funktionswert; auch die Zuweisung dieses Werts an einen anderen Namen führt den Rumpf nicht aus. Die Aufrufklammern sind auch ohne Argumente nötig.

Frage 2 von 10

Was enthält copy nach diesem Code?

const triple = function (n) { return n * 3; };
const copy = triple;
  1. Den Wert undefined, bis copy aufgerufen wird.
  2. Eine eigene Funktion mit kopiertem privatem Zustand.
  3. Die Zahl, die der Aufruf von triple(3) zurückgibt.
  4. Eine Referenz auf dieselbe Funktion wie triple.
Antwort zeigenAntwort ausblenden
Antwort: D
Die Zuweisung liest den Funktionswert in triple und speichert dieselbe Referenz in copy. Auf der rechten Seite steht kein Aufruf, daher wird kein numerisches Ergebnis berechnet. Ein späteres copy(4) würde diese Funktion aufrufen und 12 zurückgeben; das Kopieren ihrer Referenz führt sie nicht aus.

Frage 3 von 10

Was gibt adjust(8) zurück?

const adjust = (n) => { n - 3; };
  1. undefined
  2. 5
  3. 8
  4. NaN
Antwort zeigenAntwort ausblenden
Antwort: A
Die geschweiften Klammern machen dies zu einem Blockrumpf und der Block enthält kein return. Er wertet die Subtraktion aus, endet aber mit undefined. Das Ergebnis wäre 5 mit einem Ausdrucksrumpf, (n) => n - 3, oder mit einem ausdrücklichen return n - 3; innerhalb des Blocks.

Frage 4 von 10

Was gibt difference(12, 5) zurück?

function difference(first, second) {
  return first - second;
}
  1. 17
  2. undefined
  3. 7
  4. -7
Antwort zeigenAntwort ausblenden
Antwort: C
Das erste Argument setzt first auf 12 und das zweite second auf 5. Die Subtraktion in der Reihenfolge des Rumpfs ergibt 7. Das Ergebnis -7 entstünde durch Vertauschen der Argumentpositionen; Parameternamen ordnen die vom Aufruf gelieferten Werte nicht um.

Frage 5 von 10

Was gibt readLevel(0) zurück?

function readLevel(level = 40) { return level; }
  1. 40
  2. undefined
  3. null
  4. 0
Antwort zeigenAntwort ausblenden
Antwort: D
Das Argument 0 wird übergeben und ist nicht undefined, sodass es 0 bleibt. Ein Standardparameter ersetzt nicht jeden Falsy-Wert. Der ältere Ausdruck level || 40 würde 0 durch 40 ersetzen, aber dafür gilt eine andere Regel als für den Standardwert in der Definition.

Frage 6 von 10

Welche Zahl wird ausgegeben und welchen Wert hat result?

function displayTotal(a, b) { console.log(a + b); }
const result = displayTotal(6, 9);
  1. Gibt 15 aus; result ist 15.
  2. Gibt 15 aus; result ist undefined.
  3. Gibt nichts aus; result ist 15.
  4. Gibt undefined aus; result ist 15.
Antwort zeigenAntwort ausblenden
Antwort: B
Der Rumpf gibt die Summe 15 als Seiteneffekt aus. Dann erreicht er sein Ende, ohne einen Wert zurückzugeben, sodass die Zuweisung undefined speichert. Eine Zahl in der Konsole bedeutet nicht, dass der Aufruf diese Zahl zurückgegeben hat; dafür bräuchte der Rumpf return a + b;.

Frage 7 von 10

Was geschieht beim Aufruf von getFlag()?

function getFlag() {
  if (true) { let flag = "on"; }
  return flag;
}
  1. Der Aufruf löst einen ReferenceError aus.
  2. Der Aufruf löst einen TypeError aus.
  3. Der Aufruf gibt den Wert undefined zurück.
  4. Der Aufruf gibt die Zeichenkette „on“ zurück.
Antwort zeigenAntwort ausblenden
Antwort: A
Die let-Deklaration gehört zum if-Block. Bei der Rückgabe außerhalb dieses Blocks existiert keine zugängliche Deklaration von flag, sodass das Lesen einen ReferenceError auslöst. Würde let durch var ersetzt, hätte die Variable Funktionsgültigkeitsbereich und die Funktion könnte „on“ zurückgeben.

Frage 8 von 10

Welche Werte geben die beiden Aufrufe der Reihe nach zurück?

function makeStep() {
  let value = 4;
  return () => { value += 2; return value; };
}
const step = makeStep();
step();
step();
  1. 4, 6
  2. 8, 10
  3. 6, 8
  4. 6, 6
Antwort zeigenAntwort ausblenden
Antwort: C
Die Fabrik erzeugt eine mit 4 initialisierte value-Bindung. Die zurückgegebene Funktion behält Zugriff darauf, sodass der erste Aufruf sie auf 6 und der zweite dieselbe Bindung auf 8 aktualisiert. Die Rückkehr aus der Fabrik setzt die eingefangene Variable vor späteren Aufrufen nicht jeweils zurück.

Frage 9 von 10

Wie lautet die vollständige Reihenfolge der ausgegebenen Werte?

function deliver(action) { action(7); }
console.log("start");
deliver((n) => console.log(n));
console.log("end");
  1. "start", 7, "end"
  2. "start", undefined, "end"
  3. "start", "end", 7
  4. 7, "start", "end"
Antwort zeigenAntwort ausblenden
Antwort: A
deliver ruft die übergebene Pfeilfunktion sofort mit 7 auf, bevor es zum umgebenden Skript zurückkehrt. Die Ausgabe lautet daher „start“, 7, dann „end“. Ein Callback wird nicht zwangsläufig verzögert ausgeführt; diese empfangende Funktion enthält einen gewöhnlichen direkten Aufruf und keine Einplanung.

Frage 10 von 10

Was ist der endgültige Wert von result?

const values = [2, 7, 4, 9];
const result = values
  .filter((n) => n > 3)
  .map((n) => n - 1)
  .reduce((total, n) => total + n, 5);
  1. 27
  2. 22
  3. 17
  4. 21
Antwort zeigenAntwort ausblenden
Antwort: B
Der Filter behält [7, 4, 9] und die Transformation macht daraus [6, 3, 8]. Die Reduktion ab dem Anfangswert 5 ergibt 11, dann 14 und dann 22. Das Ergebnis 17 lässt den angegebenen Anfangsakkumulator weg und summiert nur die transformierten Elemente, wodurch sich die Bedeutung des letzten Aufrufs ändert.
Set 2: Wähle Eingaben und verfolge erhaltenen Zustand10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Was geschieht, wenn dieses Skript ausgeführt wird?

announce();
function announce() { console.log("Open"); }
  1. Es löst einen ReferenceError aus.
  2. Es gibt einmal undefined aus.
  3. Es gibt einmal „Open“ aus.
  4. Es löst einen TypeError aus.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Wie kann eine Pfeilfunktion ein Objekt zurückgeben, dessen Eigenschaft value gleich ihrer Eingabe n ist?
  1. (n) => ({ value: n })
  2. (n) => { return; value: n; }
  3. (n) => (value = n)
  4. (n) => { value: n }
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Was gibt der Aufruf zurück?

function combine(first, ...rest) {
  return first + rest.length;
}
const values = [8, 2, 6];
combine(...values);
  1. 16
  2. 8
  3. 11
  4. 10
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Was gibt guarded(0) zurück?

function guarded(n) {
  if (n === 0) { return "empty"; }
  return "filled";
}
  1. "filled"
  2. "empty"
  3. ["empty", "filled"]
  4. undefined
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Was gibt totalDown(3) zurück?

function totalDown(n) {
  if (n === 0) { return 0; }
  return n + totalDown(n - 1);
}
  1. 0
  2. 6
  3. 9
  4. 3
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Was geschieht beim Aufruf von read()?

let label = "outer";
function read() {
  return label;
  let label = "inner";
}
  1. Der Aufruf gibt die Zeichenkette „inner“ zurück.
  2. Der Aufruf gibt die Zeichenkette „outer“ zurück.
  3. Der Aufruf löst einen ReferenceError aus.
  4. Der Aufruf gibt den Wert undefined zurück.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Was gibt der letzte Aufruf zurück?

let word = "first";
const read = () => word;
word = "second";
function run() {
  const word = "third";
  return read();
}
run();
  1. "second"
  2. undefined
  3. "first"
  4. "third"
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Welches Array erzeugt der letzte Ausdruck?

const readers = [];
for (let i = 1; i <= 3; i++) {
  let value = i;
  readers.push(() => value);
  value += 10;
}
readers.map((read) => read());
  1. [13, 13, 13]
  2. [1, 2, 3]
  3. [14, 14, 14]
  4. [11, 12, 13]
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Was geschieht, wenn die letzte Zeile ausgeführt wird?

function speak() { console.log("Hi"); }
function twice(action) { action(); action(); }
twice(speak());
  1. Sie gibt einmal „Hi“ aus und löst dann einen TypeError aus.
  2. Sie löst einen TypeError aus, bevor sie etwas ausgibt.
  3. Sie gibt zweimal „Hi“ aus und kehrt dann normal zurück.
  4. Sie gibt dreimal „Hi“ aus und kehrt dann normal zurück.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Was ist das endgültige Ergebnis?

const compose = (f, g) => (x) => f(g(x));
const subtract = (n) => n - 2;
const triple = (n) => n * 3;
const first = compose(triple, subtract);
const second = compose(subtract, triple);
first(second(4));
  1. 18
  2. 28
  3. 24
  4. 30
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Set 3: Verbinde Aufrufe, Gültigkeitsbereiche und Callbacks10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Was geschieht, wenn dieses Skript ausgeführt wird?

const saved = build;
const build = function () { return 9; };
console.log(saved());
  1. Es löst in der ersten Zeile einen ReferenceError aus.
  2. Es löst in der letzten Zeile einen TypeError aus.
  3. Es gibt in der letzten Zeile undefined aus.
  4. Es gibt nach der abgeschlossenen Zuweisung 9 aus.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Welcher Wert wird out zugewiesen?

const describe = (n) => {
  const doubled = n * 2;
  return doubled + 1;
};
const out = describe(6);
  1. undefined
  2. 7
  3. 13
  4. 12
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Was sind items und answer nach diesem Code?

const items = [5];
function change(list) {
  list.push(8);
  list = [1];
  return list;
}
const answer = change(items);
  1. items ist [5, 8]; answer ist [5, 8].
  2. items ist [5]; answer ist [1].
  3. items ist [1]; answer ist [1].
  4. items ist [5, 8]; answer ist [1].
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Was gibt countLeaves([0, [[], 2], [[3], 4]]) zurück?

function countLeaves(list) {
  let count = 0;
  for (const item of list) {
    if (Array.isArray(item)) {
      count += countLeaves(item);
    } else {
      count++;
    }
  }
  return count;
}
  1. 6
  2. 4
  3. 3
  4. 5
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Was gibt readValue() zurück?

function readValue() {
  if (true) { var value = 14; }
  return value;
}
  1. 14
  2. undefined
  3. Der Aufruf löst einen ReferenceError aus.
  4. Der Aufruf löst einen TypeError aus.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Welche zwei Werte werden ausgegeben?

let count = 2;
function update() {
  let count = 7;
  count += 1;
  return count;
}
console.log(update());
console.log(count);
  1. 3, 3
  2. 7, 2
  3. 8, 2
  4. 8, 8
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Welche Werte geben die letzten drei Aufrufe zurück?

function makeCounter(start) {
  let n = start;
  return () => { n++; return n; };
}
const a = makeCounter(5);
const alias = a;
const b = makeCounter(5);
a();
alias();
b();
  1. 5, 6, 5
  2. 6, 7, 6
  3. 6, 7, 8
  4. 6, 6, 6
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Was gibt scale(7) zurück?

const makeScale = (factor) => (n) => n * factor;
const scale = makeScale(4);
  1. 7
  2. 11
  3. Eine neue Funktion.
  4. 28
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Was sind values und first danach?

const values = [12, 3, 20];
const first = values.sort((a, b) => a - b)[0];
  1. values ist [12, 3, 20]; first ist 3.
  2. values ist [3, 12, 20]; first ist 3.
  3. values ist [20, 12, 3]; first ist 20.
  4. values ist [12, 20, 3]; first ist 12.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Was geben die Aufrufe read(0) und read(9) der Reihe nach zurück?

function once(fn) {
  let done = false;
  let result;
  return (...args) => {
    if (!done) {
      done = true;
      result = fn(...args);
    }
    return result;
  };
}
const read = once((n) => n * 2);
  1. undefined, 18
  2. 0, 18
  3. 0, 0
  4. 0, undefined
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Set 4: Untersuche das Verhalten von Funktionen in kleinen Programmen10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Ein Skript definiert function printLine() { console.log("---"); } und führt dann printline(); aus. Was geschieht?
  1. Der Aufruf gibt undefined zurück, ohne etwas auszugeben.
  2. Eine Zeile wird ausgegeben und die Ausführung läuft weiter.
  3. Ein TypeError tritt auf und keine Zeile wird ausgegeben.
  4. Ein ReferenceError tritt auf und keine Zeile wird ausgegeben.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Was gibt der letzte Aufruf zurück?

const climb = function step(n) {
  return n === 0 ? 0 : 1 + step(n - 1);
};
const saved = climb;
saved(2);
  1. 0
  2. 2
  3. Der Aufruf löst einen ReferenceError aus.
  4. 1
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Was sind data und result am Ende?

const data = [2, 5];
function extend(list, item) {
  const next = list.concat(item);
  list.push(9);
  return next;
}
const result = extend(data, 7);
result.push(1);
  1. data ist [2, 5, 9]; result ist [2, 5, 7, 1].
  2. data ist [2, 5, 9, 1]; result ist [2, 5, 7, 1].
  3. data ist [2, 5, 7, 9]; result ist [2, 5, 7, 9, 1].
  4. data ist [2, 5]; result ist [2, 5, 7, 9, 1].
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Was gibt der letzte Aufruf zurück?

function total(base = 4, ...extras) {
  return extras.reduce((sum, n) => sum + n, base);
}
const values = [undefined, 3, 0, 8];
total(...values);
  1. NaN
  2. 11
  3. 15
  4. 19
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Welchen Wert liefert dieser Aufruf?

function read() {
  return;
}
read();
  1. undefined
  2. null
  3. false
  4. 0
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Die Eingabe n ist eine nicht negative Ganzzahl. Welcher Ersatz für die rekursive Rückgabe lässt die Funktion sowohl bei ungeraden als auch bei geraden Eingaben korrekt bis 0 herunterzählen?

function countDown(n) {
  if (n === 0) { return 0; }
  // recursive return goes here
}
  1. return 1 + countDown(n - 2);
  2. return 1 + countDown(n - 1);
  3. return 1 + countDown(n);
  4. return 1 + countDown(n + 1);
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Was gibt der letzte Aufruf zurück?

const mode = "shared";
function readMode() { return mode; }
function run() {
  const mode = "local";
  return readMode();
}
run();
  1. Der Aufruf löst einen ReferenceError aus.
  2. undefined
  3. "local"
  4. "shared"
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Welche Werte geben jobs[0]() und jobs[1]() nach der Schleife zurück?

const jobs = [];
for (var i = 0; i < 2; i++) {
  const offset = i * 10;
  jobs.push(() => offset + i);
}
  1. 12, 12
  2. 0, 10
  3. 2, 12
  4. 0, 11
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Welches Ergebnis liefert dieser Ausdruck?

["6", "8", "10"].map((text) => parseInt(text, 10));
  1. [6, NaN, NaN]
  2. [6, 8, 10]
  3. [6, NaN, 2]
  4. ["6", "8", "10"]
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Welche Werte von out und seen werden ausgegeben?

let seen = 0;
const out = [3, 5].forEach((n) => {
  seen += n;
  return n * 2;
});
console.log(out, seen);
  1. undefined, 8
  2. [6, 10], 8
  3. undefined, 16
  4. [6, 10], 0
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Set 5: Kombiniere Closures und Funktionen höherer Ordnung10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Welche Pfeilfunktion gibt die Summe ihrer beiden Eingaben zurück?
  1. (a, b) => { console.log(a + b); }
  2. (a, b) => { return a + b; }
  3. (a, b) => { return; a + b; }
  4. (a, b) => { a + b; }
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Zu welchem Wert wird add("6", 4) * 2 ausgewertet?

function add(a, b) { return a + b; }
  1. 20
  2. NaN
  3. 128
  4. "6464"
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Zu welchem Wert wird der letzte Ausdruck ausgewertet?

function choose(n) {
  if (n < 0) { return 0; }
  return n + 2;
}
choose(choose(-4));
  1. 2
  2. -2
  3. 0
  4. 4
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Was geschieht beim Aufruf von build()?

let amount = 6;
function build() {
  const read = () => amount;
  const result = read();
  let amount = 9;
  return result;
}
  1. Der Aufruf gibt die Zahl 6 zurück.
  2. Der Aufruf gibt die Zahl 9 zurück.
  3. Der Aufruf gibt den Wert undefined zurück.
  4. Der Aufruf löst innerhalb von read einen ReferenceError aus.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Welche Werte geben change(3), read() und otherRead() zurück?

function makePair() {
  let n = 1;
  return [
    (step) => { n += step; return n; },
    () => n
  ];
}
const [change, read] = makePair();
const [, otherRead] = makePair();
  1. 4, 1, 1
  2. 4, 4, 4
  3. 3, 3, 1
  4. 4, 4, 1
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Was gibt der letzte Ausdruck zurück?

function makeLimit(max) {
  let used = 0;
  return () => {
    if (used >= max) { return false; }
    used++;
    return true;
  };
}
const allow = makeLimit(2);
[allow(), allow(), allow()];
  1. [true, true, true]
  2. [true, true, false]
  3. [true, false, false]
  4. [false, false, false]
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Was sind result und calls nach diesem Code?

let calls = 0;
function choose(values, test) {
  for (const value of values) {
    calls++;
    if (test(value)) { return value; }
  }
  return null;
}
const result = choose([3, 8, 10, 5], (n) => n > 6 && n % 2 === 0);
  1. result ist 8; calls ist 2.
  2. result ist [8, 10]; calls ist 4.
  3. result ist 8; calls ist 4.
  4. result ist 10; calls ist 3.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Was sind items und out danach?

const items = [2, 4, 6];
const out = items.map((n, index, array) => {
  array[index] = n + 1;
  return n * 2;
});
  1. items ist [4, 8, 12]; out ist [3, 5, 7].
  2. items ist [3, 5, 7]; out ist [6, 10, 14].
  3. items ist [3, 5, 7]; out ist [4, 8, 12].
  4. items ist [2, 4, 6]; out ist [4, 8, 12].
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Was gibt doubleSaved(9) nach dem gezeigten früheren Aufruf zurück?

function once(fn) {
  let done = false;
  let saved;
  return (...args) => {
    if (!done) { done = true; saved = fn(...args); }
    return saved;
  };
}
const compose = (f, g) => (x) => f(g(x));
const saved = once((n) => n + 3);
const doubleSaved = compose((n) => n * 2, saved);
saved(4);
doubleSaved(9);
  1. 18
  2. 14
  3. 7
  4. 24
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Wie lautet das zurückgegebene Array?

const partial = (fn, ...fixed) => (...later) => fn(...fixed, ...later);
const offset = (base, n) => base + n;
const addFive = partial(offset, 5);
const compose = (f, g) => (x) => f(g(x));
const transform = compose((n) => n * n, addFive);
[1, 3].map(transform);
  1. [12, 16]
  2. [6, 14]
  3. [36, 64]
  4. [26, 28]
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Hängst du bei deiner eigenen Aufgabe fest? Hausaufgabenhilfe

Schlüsselbegriffe

66 Schlüsselbegriffe, jeweils mit Definition

A

Akkumulator
Ein Akkumulator ist das laufende Ergebnis einer Reduktion, das an den Callback übergeben und bei jedem Schritt durch dessen Rückgabewert ersetzt wird.
Eine Summenreduktion mit Startwert 0 aktualisiert ihren Akkumulator bei der Verarbeitung von [5, 10, 15] auf 5, 15 und 30.
Anonyme Funktion
Eine anonyme Funktion ist ein Funktionsausdruck ohne ausdrücklichen Funktionsnamen, der oft in einer Variablen gespeichert oder als Argument übergeben wird.
Der Ausdruck function (n) { return n * n; } hat in seiner geschriebenen Syntax keinen internen Funktionsnamen.
Argument
Ein Argument ist ein an einen Funktionsaufruf übergebener Wert, der nach Position einem Parameter zugeordnet oder unter den übrigen Eingaben gesammelt wird.
In area(4, 2.5) sind die Argumente 4 und 2,5, unabhängig davon, wie die Parameter benannt wurden.
arguments-Objekt
Das arguments-Objekt ist eine arrayähnliche Sammlung der an eine gewöhnliche Funktion übergebenen Eingaben und unterscheidet sich von einem echten Restparameterarray.
Älterer Code kann arguments[0] lesen, aber arguments.map nicht als gewöhnliche Arraymethode aufrufen.
Array-Destrukturierung
Array-Destrukturierung weist Einträge eines Arrays einzelnen Namen zu, sodass ein zurückgegebenes Array mehrere benannte Ergebnisse liefern kann.
Die Deklaration const [low, high] = minMax(values); entpackt die beiden Einträge des zurückgegebenen Arrays.
Aufrufstapel
Der Aufrufstapel hält nicht abgeschlossene Funktionsaufrufe fest, legt jeden neuen Aufruf oben ab und entfernt dessen Rahmen, wenn der Aufruf zurückkehrt.
Während sumOfSquares auf square(3) wartet, liegt ein Rahmen für den square-Aufruf über dem wartenden Aufruf.
Ausdrucksrumpf
Ein Ausdrucksrumpf ist der aus einem Ausdruck bestehende Rumpf einer Pfeilfunktion, dessen Wert ohne ausdrückliche return-Anweisung zum Ergebnis des Aufrufs wird.
Der Ausdrucksrumpf in (a, b) => a + b gibt die Summe der beiden übergebenen Argumente direkt zurück.

B

Basisfall
Ein Basisfall ist der Pfad zum Anhalten einer rekursiven Funktion, der ohne weiteren Selbstaufruf zurückkehrt, wenn das Problem eine einfache Form erreicht hat.
Bei einer Fakultätsfunktion für nicht negative Ganzzahlen hält die Prüfung n <= 1 die Aufrufe bei 0 oder 1 an.
Benannter Funktionsausdruck
Ein benannter Funktionsausdruck erzeugt eine Funktion mit einem internen Namen, der innerhalb ihres eigenen Rumpfs verfügbar ist, auch für rekursive Aufrufe.
In const factorial = function fact(n) { /* body */ }; ist die äußere Variable factorial und der interne Name fact.
Bindung
Eine Bindung verknüpft einen Namen in einem bestimmten Gültigkeitsbereich mit einem gespeicherten Wert, sodass gleichnamige Variablen in verschiedenen Bereichen getrennt bleiben.
Eine Zuweisung aktualisiert die nächstgelegene zugängliche Bindung; eine innere Deklaration erzeugt eine eigene lokale Bindung.
Blockgültigkeitsbereich
Ein Blockgültigkeitsbereich beschränkt eine let- oder const-Deklaration auf ihren umschließenden Block und den darin verschachtelten Code, sodass äußerer Code nicht auf diese Deklaration zugreifen kann.
Ein im Rumpf eines if deklariertes let message kann von einer Anweisung nach dem Ende dieses Blocks nicht gelesen werden.
Blockrumpf
Ein Blockrumpf enthält Anweisungen innerhalb geschweifter Klammern und braucht ein ausdrückliches return, um einen berechneten Wert aus einer Pfeilfunktion zurückzugeben.
Die Pfeilfunktion (n) => { n * 2; } berechnet ein Produkt, gibt aber undefined zurück, weil sie keine return-Anweisung hat.

C

Callback
Ein Callback ist eine Funktion, die als Argument zum Aufruf durch eine empfangende Funktion übergeben wird; diese steuert seine Eingaben und die Verwendung seines Rückgabewerts.
Eine countIf-Hilfsfunktion ruft ihren Prüfcallback für jedes Element auf und zählt diejenigen, deren Prüfergebnisse Truthy-Werte sind.
camelCase
CamelCase ist ein Benennungsstil mit einem kleingeschriebenen ersten Wort und großgeschriebenen Anfängen der folgenden Wörter, der häufig für JavaScript-Funktionsnamen verwendet wird.
Der Name calculateTotal beschreibt eine Aktion und trennt seine beiden Wörter ohne Leerzeichen oder Unterstriche.
Closure
Eine Closure verbindet eine Funktion mit erhaltenem Zugriff auf den Gültigkeitsbereich ihrer Erzeugung, sodass eingefangene Variablen nach einer äußeren Rückkehr verwendbar bleiben können.
Eine Zählerfunktion kann ihr privates count weiter erhöhen, nachdem die Fabrik, die sie erzeugt hat, beendet ist.

E

Eingefangene Variable
Eine eingefangene Variable ist eine Bindung, die über eine Closure erreicht wird, sodass die Funktion ihren aktuellen Wert statt einer eingefrorenen Kopie vom Zeitpunkt der Erzeugung lesen kann.
Wird ein äußeres greeting von „Hi“ zu „Hello“ geändert, ändert sich der Wert, den ein späterer Aufruf einer Funktion zurückgibt, die greeting liest.

F

Falsy-Wert
Ein Falsy-Wert verhält sich bei einer Wahrheitsprüfung wie false, weshalb ein älterer Standardwert mit logischem ODER zulässige Eingaben wie null ersetzen kann.
Der Ausdruck level || 50 ersetzt 0, während ein Parameterstandardwert diesen übergebenen Wert erhält.
Funktion
Eine Funktion ist eine wiederverwendbare Folge von Anweisungen, die bei ihrem Aufruf eine Aufgabe ausführt und optional Eingaben entgegennimmt und ein Ergebnis zurückgibt.
Ein Skript ruft printHeading("Report") und printHeading("Totals") auf, um dieselbe Routine für Überschriften wiederzuverwenden.
Funktion höherer Ordnung
Eine Funktion höherer Ordnung nimmt eine Funktion als Argument entgegen, gibt eine Funktion als Ergebnis zurück oder kombiniert beide Formen des Umgangs mit Funktionswerten.
Arraymethoden nehmen Callbacks entgegen, während eine Multiplikatorfabrik eine konfigurierte Berechnungsfunktion zurückgibt.
Funktionsaufruf
Ein Funktionsaufruf ruft eine Funktion mit runden Klammern nach einem Funktionswert auf; etwaige Argumente stehen innerhalb dieser Klammern.
Die Schreibweise sayHi() führt den Rumpf aus, während eine Zeile, die nur sayHi enthält, den Funktionswert liest.
Funktionsausdruck
Ein Funktionsausdruck erzeugt eine Funktion als Wert innerhalb eines Ausdrucks, etwa auf der rechten Seite einer Variablenzuweisung.
In const square = function (n) { return n * n; }; liefert der Ausdruck den Wert, der in square gespeichert wird.
Funktionsdeklaration
Eine Funktionsdeklaration definiert mit dem Schlüsselwort function eine benannte Funktion und macht sie vom Beginn ihres Gültigkeitsbereichs an verfügbar.
Der Aufruf greet("Li") kann im selben Gültigkeitsbereich oberhalb der geschriebenen Deklaration von greet stehen.
Funktionsfabrik
Eine Funktionsfabrik erzeugt konfigurierte Funktionen und gibt sie zurück; über den Zugriff einer Closure behält sie die Argumente oder den lokalen Zustand ihres Aufrufs.
Der Aufruf makeMultiplier(3) gibt eine Funktion zurück, die sich 3 merkt und später eine Zahl zum Multiplizieren entgegennimmt.
Funktionsgültigkeitsbereich
Ein Funktionsgültigkeitsbereich macht einen Parameter oder eine var-Deklaration lokal für die umschließende Funktion, selbst wenn var innerhalb eines inneren Blocks geschrieben wird.
Ein in einem if-Block zugewiesenes var result kann später aus dem umschließenden Funktionsrumpf zurückgegeben werden.
Funktionskomposition
Funktionskomposition kombiniert Funktionen, indem sie die Ausgabe einer Funktion an die Eingabe einer anderen übergibt; dabei wird der innere Aufruf vor dem äußeren ausgeführt.
Bei f(g(x)) kann die Änderung der Reihenfolge zu g(f(x)) das Ergebnis ändern, selbst wenn beide Funktionen Zahlen entgegennehmen.
Funktionsreferenz
Eine Funktionsreferenz ist ein Wert, der eine Funktion bezeichnet und es einer anderen Variablen oder empfangenden Funktion ermöglicht, dieselbe Funktion später aufzurufen.
Nach const alias = square; ruft alias(4) die Funktion auf, die auch über square erreichbar ist.
Funktionsrumpf
Ein Funktionsrumpf enthält die Anweisungen, die während eines Aufrufs ausgeführt werden; eine gewöhnliche Funktion schließt diese Anweisungen in geschweifte Klammern ein.
Enthält der Rumpf von printHeading drei Ausgabeanweisungen, werden alle drei bei jedem Aufruf dieser Funktion erneut ausgeführt.

G

Gemeinsam genutzter Zustand
Gemeinsam genutzter Zustand sind erhaltene Daten, auf die mehr als eine Funktion zugreifen und die sie aktualisieren kann, etwa eine von Funktionen aus demselben Aufruf eingefangene Bindung.
Eine zusammen zurückgegebene Einzahlungsoperation und Funktion zum Lesen des Kontostands können beide auf dasselbe private balance zugreifen.
Globaler Gültigkeitsbereich
Der globale Gültigkeitsbereich enthält Deklarationen auf oberster Ebene eines gewöhnlichen Skripts und macht diese Namen für Funktionen verfügbar, deren Gültigkeitsbereichsketten sie erreichen.
Ein count auf oberster Ebene eines Skripts kann zu gemeinsam genutztem Zustand werden, wenn mehrere Funktionen es lesen oder aktualisieren.
Gültigkeitsbereichskette
Die Gültigkeitsbereichskette ist die Folge des aktuellen und der umschließenden Gültigkeitsbereiche, die zur Auflösung eines Namens nach außen bis zur ersten Deklaration durchsucht werden.
Eine innere Berechnung liest ihr lokales amount, bevor sie eine gleichnamige Variable in einem äußeren Gültigkeitsbereich berücksichtigt.

H

Hoisting
Hoisting beschreibt, dass Deklarationen vor der Ausführung ihres Gültigkeitsbereichs registriert werden, wobei für Funktionen, var, let und const unterschiedliche Regeln für frühen Zugriff gelten.
Das Lesen eines dem Hoisting unterliegenden var vor seiner Zuweisung ergibt undefined; ein früher Aufruf einer Funktionsdeklaration kann ausgeführt werden.

I

IIFE
Ein sofort aufgerufener Funktionsausdruck, kurz IIFE, erzeugt eine Funktion und ruft sie unmittelbar auf, oft um älterem Code einen privaten Gültigkeitsbereich zu geben.
Die abschließenden Klammern in (function () { var hidden = 1; })(); rufen die neu erzeugte Funktion auf.
Inline-Funktion
Eine Inline-Funktion ist ein Funktionsausdruck, der direkt dort geschrieben wird, wo sein Wert benötigt wird, etwa eine Pfeilfunktion in der Argumentliste eines Aufrufs.
Die Pfeilfunktion in repeat(3, (i) => console.log(i)) liefert die Aktion ohne eigene benannte Deklaration.
Iterationsbindung
Eine Iterationsbindung ist die getrennte Bindung einer Schleifenvariablen, die let im Kopf einer for-Schleife für einen Schleifendurchlauf erzeugt und die Closures behalten können.
Gespeicherte Pfeilfunktionen aus verschiedenen let i-Schleifendurchläufen können nach dem Ende der Schleife unterschiedliche Werte zurückgeben.

L

Lexikalischer Gültigkeitsbereich
Der lexikalische Gültigkeitsbereich bestimmt zugängliche äußere Deklarationen anhand der geschriebenen Verschachtelung des Codes und nicht anhand der Stelle eines späteren Aufrufs.
Eine lesende Funktion auf oberster Ebene liest weiterhin ihre äußere Variable, wenn sie innerhalb einer Funktion mit einer gleichnamigen lokalen Variablen aufgerufen wird.
Lokale Variable
Eine lokale Variable gehört zu einem begrenzten Funktions- oder Blockgültigkeitsbereich, sodass ihre Arbeitsdaten von gleichnamigen äußeren Daten getrennt bleiben können.
Zwei unabhängige Funktionen können jeweils ein lokales total verwenden, ohne dass ihre Additionen einander beeinflussen.

M

Methodenkette
Eine Methodenkette verwendet den Rückgabewert einer Methode als Empfänger der nächsten Methode und ermöglicht so mehrere aufeinanderfolgende Schritte zur Arrayverarbeitung.
Ein Filter mit anschließender Transformation wählt zuerst Eingaben aus und transformiert dann nur die behaltenen Einträge.
Mutation
Eine Mutation ändert den Inhalt eines vorhandenen Werts wie eines Arrays und macht die Änderung über andere Referenzen auf dieses Array sichtbar.
Eine Funktion, die arr.push(3) aufruft, ändert dasselbe Array, das der aufrufende Code als Argument übergeben hat.

N

Neuzuweisung
Eine Neuzuweisung ändert den in einer vorhandenen Variablen oder einem Parameter gespeicherten Wert, ohne eine andere Variable zu ändern, die den alten Wert enthielt.
Innerhalb einer Funktion lässt arr = [] den Parameter auf ein anderes Array verweisen, ohne die Arrayreferenz des aufrufenden Codes zu ersetzen.

O

Objektliteral
Ein Objektliteral erzeugt mit geschweiften Klammern und benannten Eigenschaften ein Objekt; bei der Rückgabe aus einem Ausdrucksrumpf wird es in runde Klammern gesetzt.
Die Pfeilfunktion (n) => ({ value: n }) gibt ein Objekt zurück, statt ihre geschweiften Klammern als Anweisungsblock zu behandeln.
Once-Wrapper
Ein Once-Wrapper behält ein Abschlussflag und ein erstes Ergebnis, sodass spätere Aufrufe dieses Ergebnis wiederverwenden können, ohne eine abgeschlossene Operation zu wiederholen.
Ein separates Flag ermöglicht es einem Once-Wrapper, sich den Abschluss seines ersten Aufrufs zu merken, selbst wenn das Ergebnis 0 war.

P

Parameter
Ein Parameter ist ein lokaler Name in einer Funktionsdefinition, der beim Aufruf der Funktion einen Wert aus einem Argument erhält.
Die Namen width und height sind Parameter in function area(width, height) { return width * height; }.
Partielle Anwendung
Partielle Anwendung legt ausgewählte Argumente im Voraus fest und gibt eine Funktion zurück, die bei einem späteren Aufruf die übrigen Eingaben übergibt.
Eine mit „Hello“ partiell angewendete Begrüßungsfunktion kann später „Ana“ entgegennehmen und „Hello, Ana“ zurückgeben.
Pfeilfunktion
Eine Pfeilfunktion ist ein Funktionsausdruck mit einem Pfeil zwischen Parametern und Rumpf, der keine eigene this- oder arguments-Bindung hat.
Die Pfeilfunktion (n) => n * 2 liefert einen kurzen Transformationscallback für eine Arraymethode wie map.
Prädikat
Ein Prädikat ist eine Prüffunktion, deren Ergebnis entscheidet, ob eine Eingabe eine Bedingung erfüllt; es wird von Methoden wie filter oder find verwendet.
Das Prädikat (n) => n > 4 wählt aus, welche Zahlen ein Filter behält, ohne deren Werte selbst zu ändern.
Privater Zustand
Privater Zustand sind Daten innerhalb des lokalen Gültigkeitsbereichs einer Closure, auf die äußerer Code nur über die bereitgestellten Operationen zugreifen oder die er nur darüber ändern kann.
Ein Begrenzer stellt eine Operation zum Erlauben oder Ablehnen bereit und hält seinen internen Zähler used direkt unzugänglich.

R

Reine Funktion
Eine reine Funktion berechnet ihre Ausgabe allein aus ihren Argumenten und nimmt außerhalb des Aufrufs keine Änderungen vor, auch keine Konsolenausgabe.
Eine Funktion, die nur n * 2 zurückgibt, ist rein; die Ausgabe des Produkts würde einen Seiteneffekt einführen.
Rekursion
Rekursion liegt vor, wenn eine Funktion sich selbst aufruft, um kleinere Instanzen einer Aufgabe zu lösen, und ein Fall zum Anhalten keinen weiteren Selbstaufruf benötigt.
Eine Fakultätsfunktion multipliziert ihre Eingabe mit dem Ergebnis derselben Funktion für eine um eins kleinere Eingabe.
Rekursiver Fall
Ein rekursiver Fall bearbeitet das Problem, indem er dieselbe Funktion mit einer verkleinerten Eingabe aufruft und das zurückgegebene Ergebnis zum Abschluss des aktuellen Aufrufs verwendet.
Der Ausdruck n * factorial(n - 1) wartet auf die kleinere Fakultät, bevor er mit dem n dieses Aufrufs multipliziert.
Restparameter
Ein Restparameter sammelt die übrigen Argumente eines Aufrufs in einem echten Array und steht als letzter Parameter mit drei vorangestellten Punkten.
In function log(label, ...values) { /* body */ } steht das erste Argument getrennt vom Restarray.
Rückgabewert
Ein Rückgabewert ist der einzelne Wert, den ein Funktionsaufruf erzeugt und dem aufrufenden Code zum Speichern oder zur Verwendung in einem anderen Ausdruck liefert.
Gibt double(5) den Wert 10 zurück, kann der Ausdruck double(5) + 1 diesen Wert sofort verwenden, um 11 zu erzeugen.

S

Seiteneffekt
Ein Seiteneffekt ist eine Änderung außerhalb der lokalen Berechnung einer Funktion, etwa eine Ausgabe, die Änderung einer äußeren Variablen oder die Veränderung eines Eingabearrays.
Ein Callback, der mit push ein Element zu einer gemeinsam genutzten Liste hinzufügt, hat einen Seiteneffekt, selbst wenn er auch einen nützlichen Wert zurückgibt.
Spread-Syntax
Die Spread-Syntax entfaltet Arrayeinträge in einzelne Argumente eines Aufrufs, indem sie in der Argumentliste drei Punkte vor den Arrayausdruck setzt.
Der Aufruf Math.max(...[3, 7, 2]) übergibt drei Zahlen, statt ein Array an Math.max zu übergeben.
Stacktrace
Ein Stacktrace führt die Aufrufe auf, die beim Auftreten eines Fehlers aktiv waren, beginnend mit dem letzten Aufruf und fortgesetzt mit den aufrufenden Funktionen.
Wiederholte Einträge für eine rekursive Funktion können helfen, eine Abbruchbedingung zu finden, die für die Eingabe nie erreicht wird.
Standardparameter
Ein Standardparameter verwendet einen festgelegten Ausdruck, wenn sein Argument undefined ist, auch wenn der aufrufende Code dieses Argument weglässt.
Der Parameter level = 50 erhält eine ausdrücklich übergebene 0, liefert aber 50, wenn das Argument fehlt.
Stapelrahmen
Ein Stapelrahmen enthält den Ausführungszustand eines aktiven Funktionsaufrufs, einschließlich seiner Parameter, lokalen Variablen und Fortsetzungsstelle.
Zwei aktive rekursive Aufrufe können jeweils einen anderen Wert von n behalten, weil sie getrennte Rahmen haben.
Strikter Modus
Der strikte Modus ist ein JavaScript-Ausführungsmodus, der Zuweisungen an nicht deklarierte Namen zurückweist und so hilft, versehentliche globale Variablen zu verhindern.
Beginnt ein gewöhnliches Skript mit "use strict";, löst eine nicht deklarierte Zuweisung count = 5; einen ReferenceError aus.
Synchroner Callback
Ein synchroner Callback läuft während der Ausführung der empfangenden Funktion und beendet seine Arbeit, bevor diese zu ihrem eigenen aufrufenden Code zurückkehrt.
Ein map-Callback verarbeitet Arrayeinträge, bevor der abgeschlossene map-Aufruf sein Ergebnisarray liefert.

T

Transformationscallback
Ein Transformationscallback gibt für ein Eingabeelement einen Ersatzwert zurück, sodass eine transformierende Funktion die Ausgaben in einem neuen Array sammeln kann.
Der Callback (n) => n * n verwandelt die Einträge von [1, 2, 3] durch eine Transformationsoperation in Quadrate.

V

Verdeckung
Verdeckung liegt vor, wenn eine innere Deklaration innerhalb des inneren Gültigkeitsbereichs eine gleichnamige äußere Deklaration verbirgt und der Name dadurch eine andere Bindung erhält.
Ein Parameter namens count verbirgt innerhalb dieser Funktion ein äußeres count, ohne die äußere Bindung zu ändern.
Vergleichsfunktion
Eine Vergleichsfunktion ist ein Sortiercallback, dessen Rückgabewert negativ, positiv oder null ist und die relative Reihenfolge zweier verglichener Eingaben bestimmt.
Die Vergleichsfunktion (a, b) => a - b sortiert Zahlen aufsteigend, während sort das Eingabearray umordnet.
Verzögerter Callback
Ein verzögerter Callback wird für eine spätere Ausführung eingeplant, während Code nach dem einplanenden Aufruf weiterlaufen kann, ohne auf seine Ausführung zu warten.
Die Übergabe von sayHi an setTimeout lässt nachfolgende Anweisungen laufen, bevor die eingeplante Begrüßung erscheint.
Vorzeitige Rückkehr
Eine vorzeitige Rückkehr beendet einen Aufruf, bevor seine späteren Anweisungen ausgeführt werden, und behandelt oft eine besondere Eingabe vor der Hauptarbeit der Funktion.
Eine Hilfsfunktion zur Division kann bei einem Divisor von null null zurückgeben, bevor die Ausführung die Divisionsanweisung erreicht.

W

Wächterklausel
Eine Wächterklausel prüft eine besondere Bedingung und kehrt vorzeitig zurück, sodass dieser Ausnahmefall von der Hauptarbeit der Funktion getrennt bleibt.
Die Prüfung if (b === 0) { return null; } sichert die in safeDivide danach stehende Division ab.
Wrapper
Ein Wrapper ist eine zurückgegebene Funktion, die einen vorhandenen Funktionsaufruf um Verhalten ergänzt und dabei oft dessen Eingaben weiterleitet und sein Ergebnis erhält.
Ein protokollierender Wrapper gibt die übergebenen Argumente aus, ruft die ursprüngliche Funktion auf und gibt dann deren Ergebnis zurück.

Z

Zeitliche Totzone
Die zeitliche Totzone ist der Zeitraum vor der Initialisierung einer let- oder const-Bindung, in dem ein Zugriff auf diese Bindung einen ReferenceError auslöst.
Ein früher Lesezugriff findet eine verdeckende lokale Variable auch dann, wenn sie noch nicht initialisiert ist, statt einen äußeren Wert zu verwenden.

Über diese Einheit

Von StudeliFehler melden

Erstelle deine eigenen Lernmaterialien

Mach aus deinen Notizen, Folien und Dokumenten Lernleitfäden, Übungsfragen, Karteikarten und 6 weitere Tools.

Jetzt starten