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 tilvoid
, 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 typennumber
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 typennumber
og returnerer en funktion af typen(value: number) => number
.- Den indre funktion accepterer også
value
som ennumber
type og returnerer resultatet som ennumber
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 etnumber
eller etstring
. - I den indre funktion, hvis
count
overstigermax
, returneres en besked af typenstring
; ellers returneres ennumber
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 icreateLeak
, blive frigivet, når den går ud af scope, men det sker ikke, fordi closure'en fangerlargeArray
. Så længeleakyFunction
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.