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"
outerFunction
returnerer den indre funksjoneninnerFunction
.innerFunction
viser verdien til den ytre funksjonens variabelouterVariable
.
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 som returnerer en verdi av typennumber
.- Variabelen
count
er definert som typennumber
og blir manipulert innenfor closuren.
Høyereordens funksjoner
Closures er nyttige når man lager høyereordens funksjoner. Her er en høyereordens funksjon en funksjon som tar en annen funksjon som et argument eller returnerer en funksjon som et resultat. 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
er en høyereordens funksjon som lager en funksjon for å multiplisere med tallet den mottar som argument.- Den indre multiplikasjonsfunksjonen tar også et tall og returnerer resultatet av multiplikasjonen som et tall.
Eksempel på implementering av closures i TypeScript
La oss også se på et eksempel på å implementere en områdebasert teller som en lukking.
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 et tilfelle der variabelen i
deklarert med var
ikke oppfører seg 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 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.