В JavaScript, на самом деле, не "настоящие" замыкания. Я бы даже сказал, что в JS нет замыканий, а есть некая отдалённо напоминающая их эмуляция.
Суть замыканий (closures) в двух вещах:
- в теле функции можно использовать переменные, которые определены до этой функции
- значения этих переменных "фиксируются" ("запираются") в момент определения функции
Посмотрим пример на Elixir-е — "настоящем" функциональном языке:
x = 2Итак, мы внутри функции
plus_2 = fn(n) -> n + x end
plus_2.(10) # => 12
x = 3
plus_2.(10) # => 12
plus_2 используем переменную x, которая определена до этой функции.Прикол в том, что в момент определения функции значение этой переменной "фиксируется": внутри функции теперь это уже не какой-то там
x, а конкретно оказавшаяся там двойка. На место свободной переменной x, фактически, подставляется конкретное содержащееся там в момент определения значение.Поэтому в последней строке
plus_2 всё также продолжает прибавлять двойку к своему единственному аргументу n, а не новое значение x. Исходное значение x "замкнулось" внутри функции.В императивных языках, которые эмулируют эту функциональность (простите за тавтологию) функциональных языков, всё не так:
// jsКак видим, в JS (и Ruby) функция не "запомнила" (не подставила) значение переменной внутри её тела. Вместо этого, она каждый раз заново обращается к этой переменной, используя текущее значение.
let x = 2
plus_2 = n => n + x
plus_2(10) // 12
x = 3
plus_2(10) // 13
# ruby
x = 2
plus_2 = ->(n) { n + x }
plus_2.(10) # 12
x = 3
plus_2.(10) # 13
Конечно, одно из типовых использований замыканий — в "фабрике функций" (функциях, которые возвращают другие функции). И когда вы пишете код "внешней" функции (= самой фабрики), ничто не мешает тщательно контролировать самого себя на предмет того, какие переменные "утекают" во "внутреннюю" функцию (= результат работы фабрики, который будет возвращён коду, вызвавшему фабрику). И убедиться, что эти переменные никто не будет изменять вне внутренней функции, или их изменение учтено by design — например, с "неправильными" JS-замыканиями можно тривиально реализовать функцию, которая считает, сколько раз она была вызвана — а с "правильными" замыканиями это, фактически, невозможно:
// js#programming #fp
timesRunFactory = () => {
let count = 0;
return () => {
count++;
return count
}
}
timesRun = timesRunFactory();
timesRun(); // 1
timesRun(); // 2
...