W3docs

Closures

JavaScript-Closures verstehen: Wie eine Funktion ihren lexikalischen Scope speichert, mit Beispielen für Datenprivatsphäre, Funktionsfabriken, das Modulmuster und häufige Schleifenfallen.

Closures in JavaScript verstehen

In JavaScript sind Closures ein grundlegendes und mächtiges Konzept, das die Erstellung von Funktionen mit gespeicherten Daten aus ihrer lexikalischen Umgebung ermöglicht. Dieser Leitfaden bietet eine detaillierte Erkundung von Closures, ergänzt durch ausführliche Erklärungen und praktische Codebeispiele, um Ihr Verständnis zu festigen.

Was ist eine Closure?

Eine Closure ist die Kombination einer Funktion und der lexikalischen Umgebung, in der diese Funktion deklariert wurde. Diese Umgebung besteht aus allen lokalen Variablen, die zum Zeitpunkt der Erstellung der Closure im Gültigkeitsbereich waren.

Beispiel einer Closure

Betrachten Sie das folgende Beispiel:

javascript— editable

In diesem Beispiel ist greetByName eine Closure. Sie erfasst die Variablen greeting und name aus dem Gültigkeitsbereich der greet-Funktion.

Wenn die greet-Funktion mit dem Argument 'John' aufgerufen wird, erstellt sie eine lokale Variable greeting und eine Funktion greetByName. Die greet-Funktion gibt dann die greetByName-Funktion zurück. Auch nachdem die greet-Funktion fertig ausgeführt wurde, hat die zurückgegebene greetByName-Funktion weiterhin Zugriff auf die Variablen greeting und name. Das liegt daran, dass greetByName eine Closure ist, die sich die Umgebung merkt, in der sie erstellt wurde.

Warum Closures verwenden?

Closures ermöglichen die Datenkapselung und die Erstellung privater Variablen. Sie erlauben Funktionen, auch nach dem Ende der Ausführung ihres umgebenden Gültigkeitsbereichs weiterhin auf dessen Variablen zuzugreifen.

Eine einfache Closure erstellen

Betrachten Sie das folgende Beispiel:

javascript— editable

Wenn outerFunction aufgerufen wird, erstellt sie outerVariable und innerFunction. Anschließend gibt sie innerFunction zurück. Die zurückgegebene innerFunction behält den Zugriff auf outerVariable, auch nachdem outerFunction die Ausführung beendet hat.

Praktische Anwendungsfälle von Closures

Datenprivatsphäre

Closures können verwendet werden, um private Daten zu erstellen, auf die nicht aus dem globalen Gültigkeitsbereich zugegriffen werden kann.

javascript— editable

In diesem Beispiel ist count eine private Variable, auf die nur die zurückgegebene anonyme Funktion zugreifen und die sie verändern kann. Bei jedem Aufruf der Funktion wird count inkrementiert und der aktualisierte Wert zurückgegeben.

Ein subtiler, aber wichtiger Punkt: Jeder Aufruf von createCounter erzeugt eine eigene unabhängige Closure mit eigenem privaten count. Zwei separat erstellte Zähler teilen keinen Zustand:

javascript— editable

Jeder Aufruf der äußeren Funktion erstellt eine brandneue lexikalische Umgebung, sodass a und b separate Variablen inkrementieren.

Funktionsfabriken

Closures können zur Erstellung von Funktionsfabriken verwendet werden, die spezialisierte Funktionen generieren.

javascript— editable

Hier generiert createMultiplier Funktionen, die eine gegebene Zahl mit einem festgelegten multiplier multiplizieren. Jede zurückgegebene Funktion behält über die Closure Zugriff auf den multiplier-Wert.

Closures in Schleifen

Closures können bei der Verwendung innerhalb von Schleifen schwierig sein. Betrachten Sie das folgende Beispiel:

javascript— editable

In diesem Code läuft die Schleife vollständig durch, und dann werden alle drei setTimeout-Callbacks ausgeführt und geben den Wert von i aus, der nach dem Ende der Schleife 4 ist. Dies geschieht, weil var funktionsbegrenzt ist.

Um dies zu beheben, verwenden Sie let, das blockbegrenzt ist:

javascript— editable
Info

Verwenden Sie stets let oder const anstelle von var, um Gültigkeitsbereichsprobleme zu vermeiden und sicherzustellen, dass Variablen den geeigneten Gültigkeitsbereich haben.

Warum die let-Version funktioniert

Der entscheidende Punkt ist, dass let für jede Iteration der Schleife eine neue Bindung von i erstellt. Jeder setTimeout-Callback schließt über ein anderes i, sodass er sich den Wert merkt, der während seiner eigenen Iteration existierte. Mit var gibt es nur ein i, das von allen Callbacks gemeinsam genutzt wird, und wenn die Timer auslösen, hat die Schleife es bereits auf 4 erhöht.

Bevor let existierte, war die klassische Lösung, mit einem IIFE pro Iteration einen neuen Gültigkeitsbereich zu erstellen:

javascript— editable

Die sofort aufgerufene Funktion kopiert den aktuellen Wert von i in ihren eigenen Parameter captured und gibt jedem Callback eine eigene private Kopie, über die er schließen kann.

Closures und Speicherverwaltung

Closures können Speicherlecks verursachen, wenn sie nicht korrekt verwendet werden. Da Closures Referenzen auf ihren äußeren Gültigkeitsbereich behalten, können ungenutzte Variablen länger als nötig im Speicher verbleiben.

Warnung

Achten Sie auf den Speicherverbrauch beim Erstellen von Closures. Stellen Sie sicher, dass Closures keine großen Objekte oder DOM-Elemente unnötigerweise erfassen, um Speicherlecks zu verhindern.

Fortgeschrittene Closure-Muster

Modulmuster

Das Modulmuster nutzt Closures, um private und öffentliche Member innerhalb einer Funktion zu erstellen.

javascript— editable

In diesem Beispiel ist CounterModule ein sofort aufgerufener Funktionsausdruck (IIFE), der ein Objekt mit öffentlichen Methoden (increment und reset) zurückgibt. Die Variable count bleibt privat und ist nicht direkt zugänglich.

Closures in modernen JavaScript-Frameworks

Closures sind besonders wichtig in modernen JavaScript-Frameworks wie React. In React helfen Closures dabei, Zustände und Nebeneffekte in funktionalen Komponenten zu verwalten.

import React, { useState } from 'react';

function Counter() {
    const [count, setCount] = useState(0);

    function increment() {
        setCount(prevCount => prevCount + 1);
    }

    return (
        <div>
            <p>{count}</p>
            <button onClick={increment}>Increment</button>
        </div>
    );
}

In diesem Beispiel wird useState verwendet, um eine Zustandsvariable count und eine Funktion setCount zu ihrer Aktualisierung zu erstellen. Die Funktion increment bildet eine Closure und erfasst setCount und count aus dem Gültigkeitsbereich der Komponente. Hinweis: In React können Closures manchmal veraltete Zustände erfassen, wenn Abhängigkeiten in Hooks wie useEffect nicht korrekt verwaltet werden.

Info

Üben Sie das Erstellen und Verwenden von Closures in verschiedenen Kontexten, um ihr Potenzial und ihre Grenzen vollständig zu erfassen. Sie sind ein unverzichtbares Werkzeug im Werkzeugkasten jedes JavaScript-Entwicklers.

Best Practices für die Verwendung von Closures

  1. Gültigkeitsbereich begrenzen: Vermeiden Sie es, Closures in Schleifen oder tief verschachtelten Strukturen zu erstellen, sofern es nicht notwendig ist, um Speicherlecks und Performance-Probleme zu verhindern.
  2. Erfasste Variablen minimieren: Erfassen Sie innerhalb der Closure nur die Variablen, die Sie benötigen, um den Speicherverbrauch zu reduzieren.
  3. Große Erfassungen vermeiden: Erfassen Sie keine großen Objekte oder DOM-Elemente in Closures, um unnötige Speicherbindung zu verhindern.
  4. let und const verwenden: Bevorzugen Sie stets let oder const gegenüber var, um korrektes Scoping sicherzustellen und unbeabsichtigtes Verhalten zu vermeiden.
  5. Aufräumen: Bereinigen Sie wo möglich Referenzen, die von Closures erfasst wurden, um Speicherlecks zu vermeiden, insbesondere in langlebigen Anwendungen.

Fazit

Closures sind ein mächtiges Merkmal in JavaScript, das Datenprivatsphäre, Funktionsfabriken und fortgeschrittene Muster wie Module ermöglicht. Das effektive Verstehen und Einsetzen von Closures kann zu robusterem und wartbarerem Code führen. Indem Sie Closures beherrschen, verbessern Sie Ihre Fähigkeit, effizienten, sicheren und sauberen JavaScript-Code zu schreiben.

Übungen

Übung
Was sind Closures in JavaScript?
Was sind Closures in JavaScript?
Übung
Warum gibt eine 'for'-Schleife mit 'var' und setTimeout dreimal denselben Endwert aus, während 'let' die Werte 1, 2, 3 ausgibt?
Warum gibt eine 'for'-Schleife mit 'var' und setTimeout dreimal denselben Endwert aus, während 'let' die Werte 1, 2, 3 ausgibt?
Was this page helpful?