Domknięcie w TypeScript

Domknięcie w TypeScript

W tym artykule wyjaśnimy domknięcia w TypeScript.

YouTube Video

Domknięcie w TypeScript

Czym jest domknięcie?

Domknięcie oznacza możliwość zachowania odwołań do zakresu (środowiska), w którym funkcja została zdefiniowana, nawet jeśli funkcja jest wywoływana poza tym zakresem. Poniżej domknięcia zostaną wyjaśnione wraz z adnotacjami typów.

Mówiąc prościej, domknięcie łączy funkcję oraz środowisko zmiennych, w którym funkcja została zdefiniowana, co umożliwia dostęp do tego środowiska podczas wywołania funkcji.

Podstawowy mechanizm domknięć

W TypeScript, gdy funkcja jest zdefiniowana wewnątrz innej funkcji, staje się oczywiste, że funkcja wewnętrzna ma dostęp do zmiennych funkcji zewnętrznej. Oto podstawowy przykład domknięcia z adnotacjami typów.

 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"
  • Typ zwracany outerFunction to () => void, co oznacza, że zwraca ona funkcję.
  • Typ innerFunction jest jawnie ustawiony na void, co oznacza, że nie ma ona wartości zwracanej.

Zastosowania i zalety domknięć

Enkapsulacja danych

Poniżej znajduje się przykład domknięcia z adnotacjami typów służący do enkapsulacji danych.

 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
  • Funkcja createCounter zwraca funkcję typu () => number.
  • Zmienna count jest zdefiniowana jako typ number i jest modyfikowana wewnątrz domknięcia.

Funkcje wyższego rzędu

Domknięcia są przydatne podczas tworzenia funkcji wyższego rzędu. Poniżej znajduje się przykład funkcji wyższego rzędu z wyraźnymi adnotacjami typów.

 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 przyjmuje argument typu number i zwraca funkcję typu (value: number) => number.
  • Funkcja wewnętrzna również przyjmuje value jako typ number i zwraca wynik także jako typ number.

Przykład implementacji domknięcia w TypeScript

Zaimplementuj licznik z ograniczonym zakresem jako domknięcie z dodanymi adnotacjami typów.

 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"
  • Funkcja rangeCounter zwraca funkcję, która zwraca number lub string.
  • W funkcji wewnętrznej, jeśli count przekroczy wartość max, zwraca komunikat typu string; w przeciwnym razie zwraca wartość typu number.

Środki ostrożności przy używaniu domknięć

Potencjalne wycieki pamięci spowodowane przez domknięcia

Domknięcia mogą zachowywać zmienne spoza swojego zakresu, co czasami może powodować wycieki pamięci. Niepotrzebne domknięcia muszą być jawnie zwalniane z pamięci.

 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
  • W tym kodzie largeArray utworzona w createLeak powinna zostać zwolniona po wyjściu z zakresu, ale tak się nie dzieje, ponieważ domknięcie przechwytuje largeArray. Dopóki istnieje leakyFunction, ta niepotrzebna pamięć będzie nadal zajmowana.
  • Gdy obiekt lub zmienna nie są już potrzebne, ustawienie ich referencji na null pozwala mechanizmowi garbage collectora wykryć je i zwolnić pamięć.

Niewłaściwe zastosowanie domknięć w pętlach

Tworząc domknięcia wewnątrz pętli, mogą wystąpić problemy z odwoływaniem się do tej samej zmiennej. Poniższy przykład ilustruje przypadek, w którym zmienna i nie działa poprawnie.

1for (var i: number = 0; i < 3; i++) {
2    setTimeout((): void => {
3        console.log(i);
4    }, 1000);
5}
6// Output: 3, 3, 3

Kod nie daje oczekiwanego wyniku, ponieważ i odnosi się do wartości 3 na końcu pętli. Aby to naprawić, użyj let, aby rozdzielić zakres, lub zastosuj funkcję natychmiastowo wywoływaną.

1for (let i: number = 0; i < 3; i++) {
2    setTimeout((): void => {
3        console.log(i);
4    }, 1000);
5}
6// Output: 0, 1, 2

Dzięki użyciu let, zakres zmiennej i jest rozdzielony dla każdej iteracji pętli, co daje oczekiwane rezultaty.

Podsumowanie

W TypeScript domknięcia mogą prowadzić do bezpieczniejszego i bardziej przewidywalnego kodu, dzięki wykorzystaniu systemu typów. Właściwe wykorzystanie domknięć umożliwia enkapsulację danych i elastyczne projektowanie funkcji wyższego rzędu. Ponadto należy zachować ostrożność przy zarządzaniu pamięcią oraz niezamierzonych odwołaniach do zakresu podczas korzystania z domknięć.

Możesz śledzić ten artykuł, korzystając z Visual Studio Code na naszym kanale YouTube. Proszę również sprawdzić nasz kanał YouTube.

YouTube Video