Замыкания в TypeScript
В этой статье мы объясним замыкания в TypeScript.
YouTube Video
Замыкания в TypeScript
Что такое замыкание?
Замыкание — это способность сохранять ссылки на область видимости (окружение), в которой была определена функция, даже если эта функция вызывается за пределами этой области видимости. Ниже замыкания будут объяснены с учетом аннотаций типов.
Проще говоря, замыкание сочетает в себе функцию и окружение переменных, где была определена функция, что позволяет получить доступ к этому окружению при вызове функции.
Базовый механизм замыканий
В TypeScript, когда функция определяется внутри другой функции, становится очевидно, что внутренняя функция может получать доступ к переменным внешней функции. Вот базовый пример замыкания с аннотациями типов.
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возвращает внутреннюю функциюinnerFunction.innerFunctionотображает значение переменнойouterVariableвнешней функции.
Использование и преимущества замыканий
Инкапсуляция данных
Ниже приведен пример замыкания с аннотациями типов для инкапсуляции данных.
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возвращает функцию, которая возвращает значение типаnumber. - Переменная
countопределена как типnumberи управляется внутри замыкания.
Функции высшего порядка
Замыкания полезны при создании функций высшего порядка. Здесь функцией высшего порядка называется функция, которая принимает другую функцию в качестве аргумента или возвращает функцию в качестве результата. Ниже приведен пример функции высшего порядка с четкими аннотациями типов.
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— это функция высшего порядка, которая создает функцию для умножения на число, полученное в качестве аргумента.- Внутренняя функция умножения также принимает число и возвращает результат умножения в виде числа.
Пример реализации замыканий в TypeScript
Давайте также рассмотрим пример реализации счетчика на основе диапазона в виде замыкания.
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возвращает функцию, которая возвращает либоnumber, либоstring. - Во внутренней функции, если
countпревышаетmax, возвращается сообщение типаstring; в противном случае возвращается значение типаnumber.
Меры предосторожности при использовании замыканий
Потенциальные утечки памяти из-за замыканий
Замыкания могут сохранять переменные из внешней области видимости, что иногда может приводить к утечкам памяти. Ненужные замыкания необходимо явно освобождать из памяти.
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
- В этом коде
largeArray, созданный внутриcreateLeak, должен быть освобожден, когда выходит из области видимости, но этого не происходит, так как замыкание захватываетlargeArray. Пока существуетleakyFunction, эта ненужная память будет продолжать удерживаться. - Когда объект или переменная больше не нужны, их ссылка, установленная в
null, позволяет сборщику мусора обнаружить их и освободить память.
Неправильное использование замыканий в циклах
При создании замыканий внутри цикла могут возникнуть проблемы с обращением к одной и той же переменной. Следующий пример показывает случай, когда переменная i, объявленная с помощью var, ведет себя некорректно.
1for (var i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 3, 3, 3
Этот код не дает желаемого результата, так как i ссылается на значение 3 в конце цикла. Чтобы исправить это, используйте let для разделения области видимости или немедленно вызываемую функцию.
1for (let i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 0, 1, 2
Используя let, область видимости i разделяется для каждой итерации цикла, что дает ожидаемые результаты.
Резюме
В TypeScript замыкания могут позволить создавать более безопасный и предсказуемый код за счет использования системы типов. Правильное использование замыканий позволяет инкапсулировать данные и создавать гибкий дизайн функций высшего порядка. Кроме того, при использовании замыканий необходимо быть внимательным к управлению памятью и непреднамеренным ссылкам на области видимости.
Вы можете следовать этой статье, используя Visual Studio Code на нашем YouTube-канале. Пожалуйста, также посмотрите наш YouTube-канал.