Closure in TypeScript
In dit artikel leggen we closures in TypeScript uit.
YouTube Video
Closure in TypeScript
Wat is een Closure?
Closure verwijst naar het vermogen om verwijzingen naar de scope (omgeving) waarin een functie werd gedefinieerd, te behouden, zelfs als de functie buiten die scope wordt aangeroepen. Hieronder worden closures uitgelegd, inclusief type-annotaties.
Simpel gezegd combineert een closure een functie en de variabele omgeving waarin de functie werd gedefinieerd, waardoor toegang tot die omgeving mogelijk is wanneer de functie wordt aangeroepen.
Basismechanisme van Closures
In TypeScript, wanneer een functie binnen een andere functie wordt gedefinieerd, wordt het duidelijk dat de interne functie toegang heeft tot variabelen van de externe functie. Hier is een eenvoudig voorbeeld van een closure met type-annotaties.
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"
- UiterFunction geeft de innerlijke functie terug Wat?.
- De Function geeft de waarde van de variabele uit de buitenfunctie weer Variabele.
Toepassingen en Voordelen van Closures
Data-encapsulatie
Hieronder staat een voorbeeld van een closure met type-annotaties om data te encapsuleren.
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
- De functie
createCounter
retourneert een functie die een waarde van het typenumber
teruggeeft. - De variabele
count
is gedefinieerd als eennumber
type en wordt binnen de closure gemanipuleerd.
Hogere-orde Functies
Closures zijn nuttig bij het maken van hogere-orde functies. Hier is een hogere-ordefunctie een functie die een andere functie als argument neemt of een functie als resultaat retourneert. Hieronder staat een voorbeeld van een hogere-orde functie met duidelijke type-annotaties.
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
is een hogere-ordefunctie die een functie aanmaakt om te vermenigvuldigen met het getal dat het als argument ontvangt.- De interne vermenigvuldigingsfunctie neemt ook een getal aan en retourneert het resultaat van de vermenigvuldiging als een getal.
Voorbeeld van het Implementeren van Closures in TypeScript
Laten we ook kijken naar een voorbeeld van het implementeren van een bereik-gebaseerde teller als een closure.
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"
- De functie
rangeCounter
retourneert een functie die ofwel eennumber
of eenstring
retourneert. - In de interne functie, als
count
groter is danmax
, retourneert deze een bericht van het typestring
; anders retourneert het een typenumber
.
Voorzorgsmaatregelen bij het gebruik van closures
Potentiële geheugenlekken door closures
Closures kunnen variabelen uit een externe scope behouden, wat soms geheugenlekken kan veroorzaken. Onnodige closures moeten expliciet uit het geheugen worden vrijgemaakt.
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
- In deze code zou de
largeArray
die increateLeak
is aangemaakt moeten worden vrijgegeven wanneer deze buiten scope raakt, maar dat gebeurt niet omdat de closurelargeArray
vasthoudt. ZolangleakyFunction
bestaat, zal dit onnodige geheugen behouden blijven. - Wanneer een object of variabele niet langer nodig is, stelt het instellen van de referentie op
null
de garbage collector in staat dit te detecteren en het geheugen vrij te maken.
Misbruik van closures in loops
Bij het aanmaken van closures binnen een loop kunnen er problemen ontstaan met het verwijzen naar dezelfde variabele. Het volgende voorbeeld toont een geval waarin de variabele i
, gedeclareerd met var
, zich niet correct gedraagt.
1for (var i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 3, 3, 3
Deze code levert niet het gewenste resultaat op omdat i
aan het einde van de loop verwijst naar de waarde 3. Om dit op te lossen, gebruik let
om de scope te scheiden of gebruik een onmiddellijk aangeroepen functie.
1for (let i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 0, 1, 2
Door let
te gebruiken, wordt de scope van i
gescheiden voor elke iteratie van de loop, wat de verwachte resultaten oplevert.
Samenvatting
In TypeScript kunnen closures leiden tot veiliger en voorspelbaarder code door gebruik te maken van het typesysteem. Correct gebruik van closures maakt dataincapsulatie en flexibel ontwerp van hogere-orde functies mogelijk. Bovendien moet er voorzichtig worden omgegaan met geheugenbeheer en onbedoelde scope-referenties bij het gebruik van closures.
Je kunt het bovenstaande artikel volgen met Visual Studio Code op ons YouTube-kanaal. Bekijk ook het YouTube-kanaal.