Closure i TypeScript
I den här artikeln förklarar vi closures i TypeScript.
YouTube Video
Closure i TypeScript
Vad är en Closure?
Closure syftar på möjligheten att behålla referenser till den scope (miljö) där en funktion definierades, även om funktionen anropas utanför den scopen. Nedan förklaras closures, inklusive typanoteringar.
Enkelt uttryckt kombinerar en closure en funktion och den miljö av variabler där funktionen definierades, vilket möjliggör åtkomst till denna miljö när funktionen anropas.
Grundläggande mekanism för closures
I TypeScript, när en funktion definieras inom en annan funktion, blir det tydligt att den inre funktionen kan komma åt variabler från den yttre funktionen. Här är ett grundläggande exempel på en closure med typanoteringar.
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 för
outerFunction
är() => void
, vilket visar att den returnerar en funktion. - Typen för
innerFunction
är uttryckligen satt tillvoid
, vilket betyder att den inte returnerar något värde.
Användning och fördelar med closures
Datainkapsling
Nedan finns ett exempel på en closure med typanoteringar för att kapsla in 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
returnerar en funktion av typen() => number
. - Variabeln
count
är definierad somnumber
och hanteras inom closuren.
Högre ordningens funktioner
Closures är användbara när man skapar högre ordningens funktioner. Nedan är ett exempel på en högre ordningens funktion med tydliga typanoteringar.
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 ett argument av typennumber
och returnerar en funktion av typen(value: number) => number
.- Den interna funktionen tar också emot
value
somnumber
och returnerar resultatet somnumber
.
Exempel på implementering av closures i TypeScript
Implementera en intervalbegränsad räknare som en closure med tillagda typanoteringar.
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
returnerar en funktion som returnerar antingennumber
ellerstring
. - I den interna funktionen, om
count
överstigermax
, returneras ett meddelande avstring
-typ, annars returneras ettnumber
-värde.
Försiktighetsåtgärder vid användning av closures
Möjliga minnesläckor från closures
Closures kan behålla variabler från ett yttre scope, vilket ibland kan orsaka minnesläckor. Onödiga closures måste explicit rensas från 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 denna kod ska
largeArray
som skapats icreateLeak
frisläppas när den går utanför scope, men det sker inte eftersom closuren fångarlargeArray
. Så längeleakyFunction
finns kvar kommer detta onödiga minne att hållas kvar. - När ett objekt eller en variabel inte längre behövs, kan dess referens sättas till
null
så att garbage collectorn kan frigöra minnet.
Felaktig användning av closures i loopar
Vid skapande av closures inuti en loop kan det uppstå problem med att hänvisa till samma variabel. Följande exempel visar ett fall där variabeln i
inte fungerar korrekt.
1for (var i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 3, 3, 3
Den här koden ger inte önskat resultat eftersom i
refererar till värdet 3 vid slutet av loopen. För att åtgärda detta, använd antingen let
för att separera scope eller använd en omedelbart anropad funktion.
1for (let i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 0, 1, 2
Genom att använda let
separeras scopen för i
för varje iteration och ger de förväntade resultaten.
Sammanfattning
I TypeScript kan closures leda till säkrare och mer förutsägbar kod genom att dra nytta av typsystemet. Korrekt användning av closures möjliggör datainkapsling och flexibel design av högre ordningens funktioner. Dessutom måste man vara noggran med minneshantering och oavsiktliga scope-referenser när man använder closures.
Du kan följa med i artikeln ovan med hjälp av Visual Studio Code på vår YouTube-kanal. Vänligen kolla även in YouTube-kanalen.