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"
  • outerFunction returnerer den indre funktion innerFunction.
  • innerFunction viser værdien af den ydre funktions variabel outerVariable.

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
  • createCounter-funktionen returnerer en funktion, der returnerer en værdi 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. Her er en højere-ordens funktion en funktion, der tager en anden funktion som et argument eller returnerer en funktion som resultat. 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 er en højere-ordens funktion, der opretter en funktion til at multiplicere med det tal, den modtager som argument.
  • Den indre multiplikationsfunktion tager også et tal og returnerer resultatet af multiplikationen som et tal.

Eksempel på implementering af closures i TypeScript

Lad os også se på et eksempel på at implementere en intervalbaseret tæller som en lukning.

 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 viser et tilfælde, hvor variablen i erklæret med var ikke opfører sig 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