Closure in TypeScript

Closure in TypeScript

In diesem Artikel erklären wir Closures in TypeScript.

YouTube Video

Closure in TypeScript

Was ist eine Closure?

Closure bezeichnet die Fähigkeit, Referenzen auf den Gültigkeitsbereich (Umgebung) zu behalten, in dem eine Funktion definiert wurde, selbst wenn die Funktion außerhalb dieses Bereichs aufgerufen wird. Im Folgenden werden Closures einschließlich Typannotationen erklärt.

Einfach ausgedrückt kombiniert eine Closure eine Funktion mit der Variablenumgebung, in der die Funktion definiert wurde, und ermöglicht den Zugang zu dieser Umgebung, wenn die Funktion aufgerufen wird.

Grundmechanismus von Closures

In TypeScript wird deutlich, dass eine innerhalb einer anderen Funktion definierte Funktion auf Variablen der äußeren Funktion zugreifen kann. Hier ist ein einfaches Beispiel für eine Closure mit Typannotationen.

 1function outerFunction(): () => void {
 2    let outerVariable: string = "I am from outer function";
 3
 4    function innerFunction(): void {
 5        // The inner function accesses the variable of the outer function
 6        console.log(outerVariable);
 7    }
 8
 9    return innerFunction;
10}
11
12const closure: () => void = outerFunction();
13closure();  // "I am from outer function"
  • outerFunction gibt die innere Funktion innerFunction zurück.
  • innerFunction zeigt den Wert der Variablen outerVariable der äußeren Funktion an.

Anwendungsfälle und Vorteile von Closures

Datenkapselung

Nachfolgend ein Beispiel für eine Closure mit Typannotationen zur Datenkapselung.

 1function createCounter(): () => number {
 2    let count: number = 0;
 3
 4    return function (): number {
 5        count += 1;
 6        return count;
 7    };
 8}
 9
10const counter: () => number = createCounter();
11console.log(counter());  // 1
12console.log(counter());  // 2
13console.log(counter());  // 3
  • Die Funktion createCounter gibt eine Funktion zurück, die einen Wert vom Typ number zurückgibt.
  • Die Variable count ist als Typ number definiert und wird innerhalb der Closure verarbeitet.

Höherwertige Funktionen

Closures sind nützlich beim Erstellen von höherwertigen Funktionen. Hier ist eine höhere Funktion eine Funktion, die eine andere Funktion als Argument nimmt oder eine Funktion als Ergebnis zurückgibt. Nachfolgend ein Beispiel für eine höherwertige Funktion mit klaren Typannotationen.

 1function createMultiplier(multiplier: number): (value: number) => number {
 2    return function (value: number): number {
 3        return value * multiplier;
 4    };
 5}
 6
 7const double: (value: number) => number = createMultiplier(2);
 8console.log(double(5));  // 10
 9
10const triple: (value: number) => number = createMultiplier(3);
11console.log(triple(5));  // 15
  • createMultiplier ist eine höhere Funktion, die eine Funktion erzeugt, um mit der Zahl zu multiplizieren, die sie als Argument erhält.
  • Die innere Multiplikationsfunktion nimmt ebenfalls eine Zahl als Argument und gibt das Ergebnis der Multiplikation als Zahl zurück.

Beispiel für die Implementierung von Closures in TypeScript

Schauen wir uns auch ein Beispiel für die Implementierung eines bereichsbasierten Zählers als Closure an.

 1function rangeCounter(min: number, max: number): () => number | string {
 2    let count: number = min;
 3
 4    return function (): number | string {
 5        if (count <= max) {
 6            return count++;
 7        } else {
 8            return `Count has exceeded the maximum value: ${max}`;
 9        }
10    };
11}
12
13const counter: () => number | string = rangeCounter(1, 5);
14
15console.log(counter());  // 1
16console.log(counter());  // 2
17console.log(counter());  // 3
18console.log(counter());  // 4
19console.log(counter());  // 5
20console.log(counter());  // "Count has exceeded the maximum value: 5"
  • Die Funktion rangeCounter gibt eine Funktion zurück, die entweder eine number oder einen string zurückgibt.
  • In der internen Funktion wird, wenn count den Wert von max überschreitet, eine Nachricht vom Typ string zurückgegeben, andernfalls ein Wert vom Typ number.

Vorsichtsmaßnahmen beim Einsatz von Closures

Potentielle Speicherlecks durch Closures

Closures können Variablen aus einem äußeren Gültigkeitsbereich behalten, was gelegentlich zu Speicherlecks führen kann. Nicht benötigte Closures sollten explizit aus dem Speicher entfernt werden.

 1function createLeak(): () => void {
 2    // Large array consuming significant memory
 3    const largeArray: string[] = new Array(1000000).fill("leak");
 4
 5    // Closure capturing `largeArray`
 6    return function (): void {
 7        console.log(largeArray[0]); // Using the captured array
 8    };
 9}
10
11// Create a closure that holds a reference to the large array
12let leakyFunction = createLeak();
13
14// The large array is not released as `leakyFunction` still references it
15
16// When the object is no longer needed
17leakyFunction = null; // Explicitly remove the reference
  • In diesem Code sollte das in createLeak angelegte largeArray freigegeben werden, wenn es außerhalb des Gültigkeitsbereichs liegt, aber das geschieht nicht, da die Closure largeArray einfängt. Solange leakyFunction existiert, wird dieser unnötige Speicher weiterhin belegt bleiben.
  • Wenn ein Objekt oder eine Variable nicht mehr benötigt wird, ermöglicht das Setzen des Verweises auf null dem Garbage Collector, sie zu erkennen und den Speicher freizugeben.

Missbrauch von Closures in Schleifen

Beim Erstellen von Closures innerhalb einer Schleife kann es zu Problemen kommen, da auf dieselbe Variable verwiesen wird. Das folgende Beispiel zeigt einen Fall, in dem sich die mit var deklarierte Variable i nicht korrekt verhält.

1for (var i: number = 0; i < 3; i++) {
2    setTimeout((): void => {
3        console.log(i);
4    }, 1000);
5}
6// Output: 3, 3, 3

Dieser Code liefert nicht das gewünschte Ergebnis, da i am Ende der Schleife auf den Wert 3 verweist. Um dies zu beheben, verwenden Sie entweder let, um den Gültigkeitsbereich zu trennen, oder nutzen Sie eine sofort ausgeführte Funktion.

1for (let i: number = 0; i < 3; i++) {
2    setTimeout((): void => {
3        console.log(i);
4    }, 1000);
5}
6// Output: 0, 1, 2

Durch die Verwendung von let wird für jede Iteration der Schleife ein eigener Gültigkeitsbereich für i erzeugt, was die erwarteten Ergebnisse liefert.

Zusammenfassung

In TypeScript können Closures dank des Typsystems zu sichererem und vorhersehbarerem Code führen. Der richtige Einsatz von Closures ermöglicht Datenkapselung und eine flexible Gestaltung von höherwertigen Funktionen. Zudem ist beim Einsatz von Closures auf die Speicherverwaltung und unbeabsichtigte Bereichsreferenzen zu achten.

Sie können den obigen Artikel mit Visual Studio Code auf unserem YouTube-Kanal verfolgen. Bitte schauen Sie sich auch den YouTube-Kanal an.

YouTube Video