Замыкание / Сlosure
Замыкания — это функции, ссылающиеся на независимые (свободные) переменные. Другими словами, функция,
определённая в замыкании, «запоминает» окружение, в котором она была создана.
Независимые переменные — это все переменные, которые не были переданы как параметры и не были объявлены
как локальные.
Для понимания замыкания рассмотрим фазы жизненного цикла функции
Создание функции
Создание функции состоит из описания - сигнатура + блок, а также записи свойств this + [[Scope]]
Свойство this указывает на объект, в котром находится функция
Свойство [[Scope]] записывается
окружение функции вне самой функции (т.е. ИМЕНА независимых переменных)
Присвоение функции переменной
function sum (a) {
return a + x;
}
let add = sum;
add(2)
let x = 0;
function sum (a) {
return a + x;
}
let add = sum;
add(2)
x = 2;
add(2)
При присвоении функции переменной (т.е. присвоение имени без вызова функции) происходит передача
ссылки на объект функции.
Другимим словами - при присвоении функции переменной этой самой переменной передается и свойстов
[[Scope]]
со ссылкой на окружение функции на момент создания. Т.е. присвоение по ссылке - это не копирование.
Вызов функции
При вызове функции происходит создание и заполнение arguments и
LexicalEnvironment или лексического окружения .
Лексическое окружение - это все доступные ЗНАЧЕНИЯ имен независимых переменных.
В LexicalEnvironment при вызове также записвается
и [[Scope]] с окружением на момент создания.
Другими словами - функция помнит ИМЕНА окружения на момент своего создания и заполняет их
ЗНАЧЕНИЯМИ на момент вызова.
Последовательность поиска - поиск значений осуществляется поиском имен внутри функции,
а затем вне функции - последовательно поднимаясь из области видимости каждой вложенности до глобального объекта
поиска.
Область видимости
const common
= "Видима всем в window";
function Foo () {
const fooScope
= "Видима "
+ "только тем, "
+ "кто внутри Foo";
function Bar () {
const barScope
= "Видима "
+"только тем, "
+ "кто внутри Bar";
}
}
Подъем видимости
const common = 100;
function Foo () {
const common = 200;
function Bar () {
const common = 300;
return common;
}
return Bar;
}
После выполнения функции все ее LexicalEnvironment (значения независимых переменных и аргументы) удаляются
Создаем замыкание
Каррироваие - это просто
Каррирование - это способ сделать из вызова foo(a,b,c) вызов foo(a)(b)(c)
Сделать это очень просто - нужно сделать внутри функции return другой функции с одним из параметров. А затем можно повторить return с новым параметром вунтри вложенной функции.
Т.е. сколько параметров - столько и возвратов вложенных функций
Каррироваие
function pow(base) {
function mult (exponent) {
return base ** exponent;
}
return mult;
}
const toPow = pow;
toPow(2)
toPow(2)(4);
Частичное применение - это спостоб присвоить одному из параметров постоянное значение для новой функции
Частичное применение
function pow(base) {
function mult (exponent) {
return base ** exponent;
}
return mult;
}
const twoPow = pow(2);
twoPow(3);
Проверим видимость переменных
const common = 100;
function Foo () {
const common = 200;
function Bar () {
const common = 300;
return common;
}
return Bar;
}
const Ups = Foo;
Ups()
Ups()()
Замыкание
Счетчик на основе замыкания
function addOne () {
let inCounter = 0;
function increment() {
return inCounter += 1;
}
function set(val) {
inCounter = val;
}
function get() {
return inCounter;
}
return increment;
}
const uno = addOne();
uno();
uno();
uno();
const duo = addOne();
duo();
duo();
duo();