Closure i TypeScript
I denne artikkelen vil vi forklare closures i TypeScript.
YouTube Video
Closure i TypeScript
Hva er en closure?
Closure refererer til evnen til å beholde referanser til miljøet (scope) der en funksjon ble definert, selv om funksjonen blir kalt utenfor dette miljøet. Nedenfor vil closures bli forklart inkludert typeannotasjoner.
Enkelt sagt kombinerer en closure en funksjon og variabelmiljøet hvor funksjonen ble definert, slik at man får tilgang til det miljøet når funksjonen kalles.
Grunnleggende mekanisme for closures
I TypeScript, når en funksjon er definert inne i en annen funksjon, blir det tydelig at den indre funksjonen har tilgang til variablene til den ytre funksjonen. Her er et grunnleggende eksempel på en closure med typeannotasjoner.
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 til
outerFunction
er() => void
, noe som indikerer at den returnerer en funksjon. - Typen til
innerFunction
er eksplisitt satt tilvoid
, som betyr at den ikke har noen returverdi.
Bruk og fordeler med closures
Dataenkapsulering
Nedenfor er et eksempel på en closure med typeannotasjoner for innkapsling av 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
-funksjonen returnerer en funksjon av typen() => number
.- Variabelen
count
er definert som typennumber
og blir manipulert innenfor closuren.
Høyereordens funksjoner
Closures er nyttige når man lager høyereordens funksjoner. Nedenfor er et eksempel på en høyereordens funksjon med tydelige typeannotasjoner.
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
tar et argument av typennumber
og returnerer en funksjon av typen(value: number) => number
.- Den interne funksjonen tar også
value
som ennumber
-type og returnerer resultatet som ennumber
-type.
Eksempel på implementering av closures i TypeScript
Implementer en grensebasert teller som en closure med ekstra typeannotasjoner.
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"
rangeCounter
-funksjonen returnerer en funksjon som returnerer enten etnumber
eller enstring
.- I den interne funksjonen, hvis
count
overskridermax
, returnerer den en melding av typenstring
; ellers returnerer den ennumber
-type.
Forholdsregler ved bruk av closures
Potensielle minnelekkasjer fra closures
Closures kan beholde variabler fra et eksternt scope, noe som noen ganger kan føre til minnelekkasje. Unødvendige closures må eksplisitt frigjøres fra minnet.
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 koden skal
largeArray
som lages inne icreateLeak
egentlig frigjøres når den går ut av scope, men det skjer ikke fordi closuren fangerlargeArray
. Så lengeleakyFunction
eksisterer, vil dette unødvendige minnet fortsatt bli beholdt. - Når et objekt eller en variabel ikke lenger trengs, kan referansen settes til
null
, slik at søppeloppsamleren kan oppdage den og frigjøre minnet.
Feilbruk av closures i løkker
Når du lager closures inne i en løkke, kan det oppstå problemer med referanse til samme variabel. Følgende eksempel viser en situasjon der variabelen i
ikke fungerer slik den skal.
1for (var i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 3, 3, 3
Denne koden gir ikke ønsket resultat fordi i
refererer til verdien 3 på slutten av løkken. For å fikse dette, bruk enten let
for å skille scopes eller bruk en umiddelbart påkalt funksjon.
1for (let i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 0, 1, 2
Ved å bruke let
blir scopet til i
separert for hver løkkeiterasjon, noe som gir forventede resultater.
Sammendrag
I TypeScript kan closures føre til sikrere og mer forutsigbar kode ved å bruke typesystemet. Korrekt bruk av closures muliggjør dataenkapsulering og fleksibel design av høyereordens funksjoner. I tillegg må man være oppmerksom på minnehåndtering og utilsiktede scope-referanser ved bruk av closures.
Du kan følge med på artikkelen ovenfor ved å bruke Visual Studio Code på vår YouTube-kanal. Vennligst sjekk ut YouTube-kanalen.