Вложенные функции, замыкания и область действия
Я пытался обернуть голову вокруг прицела, особенно закрытия. Я знаю, что есть много постов на эту тему, и я много читаю. Но большинство мест называют эту тему продвинутой и используют терминологию, которую довольно трудно понять. Мне бы хотелось быть абсолютно уверенным, что я правильно понял основы, чтобы не углубляться в более сложные темы с неправильным представлением о том, как на самом деле работают функции.
Итак... Я выбрал основную функцию и очень хотел бы, чтобы кто-то сказал мне, действительно ли то, что я думаю, происходит под капотом.
Это код:
function sum(a) {
return function(b) {
return a+b
}
}
console.log( sum(1)(sum(2)))
(Я знаю, что на самом деле это не сумма, я был настроен, чтобы попытаться понять, что происходит на каждом этапе.)
Итак, мое главное сомнение было в том, почему А было 1, а не 2. Я пришел к выводу, что замыкание создается, как только function(b)
создан, чтобы принять sum(2)
в качестве аргумента, сразу после возвращения sum(1)
, Поэтому, согласно определению замыкания, я предполагаю, что во время создания функции она также сохраняет лексическую среду (в которой a = 1
). Это правильно?
Я сделал схему шагов.
3 ответа
Когда sum
называется это создает область. В этой области есть формальный параметр a
и внутренняя анонимная функция, которая немедленно возвращается. Эта анонимная функция является закрытием, потому что она захватывает область действия своей включающей функции (sum
). Следовательно, эта внутренняя функция имеет доступ к a
,
Теперь мы подошли к тому, что, по-видимому, вас смущает: внутренняя функция получает только копию объема суммы, а не ссылку на исходную. Это означает, что если мы вернемся из sum
и, таким образом, исключить его область, эта копия остается неизменной (a
внутренней функции остается 1). Дальнейшие вызовы функций sum
с другими аргументами также не влияют на закрытие.
Вывод: замыкание может существовать дольше, чем его окружающая функция.
Технически говоря a
из sum
хранится в стеке, тогда как захваченный a
закрытия хранится в куче и, таким образом, не зависит от времени жизни sum
,
Кстати, то, что вы здесь делаете, называется карри. Вместо звонка sum
с несколькими аргументами вы вызываете это процедурно, с одним аргументом на вызов:
sum(1, 2); // multi argument form
sum(1)(2); // curry form
Независимо от того, какое средство мы используем для транспортировки внутренней функции за пределы ее лексической области, оно будет поддерживать ссылку на область, где она была первоначально объявлена, и везде, где мы ее выполняем, это закрытие будет осуществляться.
В следующей функции вы увидите, как greet
будет использовать переменную salute
объявлено внутри greeting
функция, даже если это больше не вызывается, это называется закрытием.
function greeting(name) {
var salute = "Hello "; // This is a closure
return function() {
console.log(salute + name);
}
}
var greet = greeting("Dave");
greet(); // Hello Dave
Вы можете узнать больше о замыканиях в серии книг Кайла Симпсона " Вы не знаете JS", но для этой конкретной темы отметьте " Вы не знаете JS: Область применения и замыкания". Он использует простой и точный язык для объяснения таких сложных понятий, как этот.
Вот что происходит
1) Если функция возвращает функцию и эта возвращаемая функция вызывается немедленно, она вызывается currying
(Функциональный термин программирования). Вы смешали currying
а также closure
обе концепции в этом примере.
2) Сначала твой sum(1)
часть называется. который вернется function(b) {return a+b}
(давайте отнесем это как #1), но с помощью он будет оставаться в живых a
как 1 для только для контекста #1.
3) Поскольку аргумент функции является самим вызовом функции, эта часть аргумента будет вызвана. например sum(1)(sum(2))
, Вот sum(2)
часть будет вызвана и возвращается function(b) {return a+b}
(давайте назовем это # 2), также это будет поддерживать a
как 2 для контекста только #2 (закрытие).
4) Теперь мы немедленно вызываем #1st с #2nd в качестве параметра с этим синтаксисом каррирования - #1st (#2nd)
5) так наш a
является нераспределенной переменной и имеет значение 1
, b
переменная имеет значение function(b) {return a+b}
, Поскольку мы объединяем эти два, итоговый результат 1function(b) {return a+b}
NB - a) Если вы хотите получить сумму a+b, а не странный результат, просто измените вашу последнюю строку как console.log(sum(1)(2))
, б) Если вы заметили закрытие a
значение 2 в функции, обозначенной #2nd, никогда не используется нигде, кроме как живым.