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"
  • Der Rückgabetyp von outerFunction ist () => void, was bedeutet, dass sie eine Funktion zurückgibt.
  • Der Typ von innerFunction ist explizit auf void gesetzt, was darauf hinweist, dass sie keinen Rückgabewert hat.

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 vom Typ () => number zurück.
  • 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. 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 nimmt ein Argument vom Typ number entgegen und gibt eine Funktion vom Typ (value: number) => number zurück.
  • Die interne Funktion nimmt ebenfalls value als Typ number entgegen und gibt das Ergebnis als Typ number zurück.

Beispiel für die Implementierung von Closures in TypeScript

Implementieren Sie einen Bereichszähler als Closure mit erweiterten Typannotationen.

 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 die Variable i nicht korrekt funktioniert.

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