Замыкания

Последнее обновление: 02.04.2023

Замыкание (closure) представляет объект функции, который запоминает свое лексическое окружение даже в том случае, когда она выполняется вне своей области видимости.

Технически замыкание включает три компонента:

  • внешняя функция, которая определяет некоторую область видимости и в которой определены некоторые переменные и параметры - лексическое окружение

  • переменные и параметры (лексическое окружение), которые определены во внешней функции

  • вложенная функция, которая использует переменные и параметры внешней функции

Function outer(){       // внешняя функция
    var n;              // некоторая переменная - лексическое окружение
    Function inner(){     // вложенная функция
        // действия с переменной n
    }
	return inner;
}

Рассмотрим замыкания на простейшем примере:

Function outer(){       // внешняя функция
    var n = 5;         // некоторая переменная - лексическое окружение функции inner
    void inner(){     // вложенная функция
        // действия с переменной n
		n++;
		print(n);
    }
	return inner;
}
void main() {
	
	Function fn = outer();   // fn = inner, так как функция outer возвращает функцию inner
	// вызываем внутреннюю функцию inner
	fn();   // 6
	fn();   // 7
	fn();   // 8
}

Здесь функция outer задает область видимости, в которой определены внутренняя функция inner и переменная n. Переменная n представляет лексическое окружение для функции inner. В самой функции inner инкрементируем переменную n и выводим ее значение на консоль. В конце функция outer возвращает функцию inner.

Далее вызываем функцию outer:

Function fn = outer();

Поскольку функция outer возвращает функцию inner, то переменная fn будет хранить ссылку на функцию inner. При этом эта функция запомнила свое окружение - то есть внешнюю переменную n.

Далее мы фактически три раза вызываем функцию inner, и мы видим, что переменная n, которая определена вне функции inner, увеличивается на единицу:

fn();   // 6
fn();   // 7
fn();   // 8

То есть несмотря на то, что переменная n определена вне функции inner, эта функция запомнила свое окружение и может его использовать, несомотря на то, что она вызывается вне функции outer, в которой была определена. В этом и суть замыканий.

Кстати мы можем сократить определение функции outer, используя анонимную функцию:

Function outer(){
    var n = 5;
    return (){
		n++;
		print(n);
    };
}

Рассмотрим другой пример:

Function multiply(int n){

    return (int m) => n * m;
}
void main() {
	
	Function func = multiply(5);
	int result1 = func(6); // 30
	print(result1); 	   // 30
 
	int result2 = func(5); // 25
	print(result2);		  // 25
}

Итак, здесь вызов функции multiply() приводит к вызову другой внутренней функции. Внутренняя же функция:

(int m) => n * m;

запоминает окружение, в котором она была создана, в частности, значение параметра n.

В итоге при вызове функции multiply определяется переменная func, которая и представляет собой замыкание, то есть объединяет две вещи: функцию и окружение, в котором функция была создана. Окружение состоит из любой локальной переменной или любого параметра, которые были определены в области действия функции multiply во время создания замыкания.

То есть result1 — это замыкание, которое содержит и внутреннюю функцию (int m) => n * m;, и параметр n, который существовал во время создания замыкания.

При этом важно не запутаться в параметрах. При определении замыкания:

Function func = multiply(5);

Число 5 передается для параметра n функции multiply.

При вызове внутренней функции:

int result1 = func(6); // 30

Число 6 передается для параметра m во внутреннюю функцию (int m) => n * m;

Также мы можем использовать другой вариант для вызова замыкания:

Function multiply(int n){
    return (int m) => n * m;;
}
void main() {
	
	int result1 = multiply(5)(6); // 30
	print(result1); 	   // 30
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850