Closure i TypeScript

Closure i TypeScript

I denne artikel forklarer vi closures i TypeScript.

YouTube Video

Closure i TypeScript

Hvad er en closure?

Closure refererer til evnen til at bevare referencer til det scope (miljø), hvor en funktion blev defineret, selv hvis funktionen kaldes uden for dette scope. Nedenfor forklares closures, inklusive typeannoteringer.

Enkelt sagt kombinerer en closure en funktion og det variable miljø, hvor funktionen blev defineret, hvilket tillader adgang til dette miljø, når funktionen kaldes.

Grundlæggende mekanisme for closures

I TypeScript bliver det tydeligt, at en indre funktion kan få adgang til variabler fra den ydre funktion, når en funktion er defineret inden i en anden funktion. Her er et grundlæggende eksempel på en closure med typeannoteringer.

 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"
  • Returtypen for outerFunction er () => void, hvilket angiver, at den returnerer en funktion.
  • Typen af innerFunction er eksplicit sat til void, hvilket betyder, at den ikke returnerer nogen værdi.

Anvendelser og fordele ved closures

Dataindkapsling

Nedenfor ses et eksempel på en closure med typeannoteringer til at indkapsle data.

 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
  • Funktionen createCounter returnerer en funktion af typen () => number.
  • Variablen count er defineret som typen number og manipuleres inden for closure.

Højere-ordens-funktioner

Closures er nyttige, når man opretter højere-ordens-funktioner. Nedenfor er et eksempel på en højere-ordens-funktion med tydelige typeannoteringer.

 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 tager et argument af typen number og returnerer en funktion af typen (value: number) => number.
  • Den indre funktion accepterer også value som en number type og returnerer resultatet som en number type.

Eksempel på implementering af closures i TypeScript

Implementér en range-begrænset tæller som en closure med tilføjede typeannoteringer.

 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"
  • Funktionen rangeCounter returnerer en funktion, som returnerer enten et number eller et string.
  • I den indre funktion, hvis count overstiger max, returneres en besked af typen string; ellers returneres en number type.

Forholdsregler ved brug af closures

Potentielle hukommelseslækager fra closures

Closures kan bevare variabler fra et ydre scope, hvilket nogle gange kan forårsage hukommelseslækager. Unødvendige closures skal eksplicit frigives fra hukommelsen.

 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
  • I denne kode burde largeArray, som oprettes inden i createLeak, blive frigivet, når den går ud af scope, men det sker ikke, fordi closure'en fanger largeArray. Så længe leakyFunction eksisterer, vil denne unødvendige hukommelse fortsat blive bevaret.
  • Når et objekt eller en variabel ikke længere er nødvendig, giver det garbage collectoren mulighed for at frigive hukommelsen, hvis dens reference sættes til null.

Misbrug af closures i løkker

Når der oprettes closures inde i en løkke, kan der opstå problemer med at referere til den samme variabel. Følgende eksempel demonstrerer et tilfælde, hvor variablen i ikke fungerer korrekt.

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

Denne kode giver ikke det ønskede resultat, fordi i refererer til værdien 3 i slutningen af løkken. For at løse dette skal du enten bruge let til at adskille scopet eller bruge en straks-invokeret funktion.

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

Ved at bruge let adskilles scopet for i ved hver iteration af løkken, hvilket giver de forventede resultater.

Sammendrag

I TypeScript kan closures føre til mere sikker og forudsigelig kode ved at udnytte typesystemet. Korrekt brug af closures muliggør dataindkapsling og fleksibelt design af højere-ordens-funktioner. Derudover skal man være opmærksom på hukommelsesstyring og utilsigtede scope-referencer, når man bruger closures.

Du kan følge med i ovenstående artikel ved hjælp af Visual Studio Code på vores YouTube-kanal. Husk også at tjekke YouTube-kanalen.

YouTube Video