Чтобы начать с небольшой предыстории, я начал программировать три года назад. Первые два года я работал универсалом. Пишу что угодно от SQL до CSS. За последний год у меня была возможность и удовольствие сосредоточиться на JavaScript. В результате я влюбился в язык; у него есть выразительная сила, которая действительно говорит со мной. Так что стало очевидно, что мне нужно копнуть глубже и изучить концепции Core/Advanced в JavaScript.

Я только что закончил читать первую книгу Кайла Симпсона из серии Ты не знаешь JS. Под названием Области действия и замыкания. Тема замыканий также несколько раз поднималась в блогах, за которыми я слежу. Таким образом, в качестве упражнения в обзоре и для расширения моих знаний по этому вопросу, а также, надеюсь, для понимания других. Я думал, что напишу статью о замыканиях.

Мой 10000-футовый взгляд на замыкания заключается в том, что замыкание в JavaScript — это механизм, который дает функции способ запомнить среду (лексическую область действия), в которой она была создана. Итак, если у вас есть функция, внутри которой объявлена ​​переменная, а затем она возвращает функцию. Эта функция сохраняет доступ к переменной. Даже после того, как объемлющая функция вернулась. Вот типичный пример

function foo() {
   var baz = "norf";
  
   function bar(){
     
     return baz;
   }
   
   return bar;
}
var qux = foo();
qux(); // returns "norf"

qux присваивается возвращаемое значение foo, которое равно bar. Когда вы вызываете qux, он возвращает значение baz, даже если foo уже выполняется. Тада, это все люди. Вот что такое закрытие.

ну, это определение правдиво, но наивно и приводит меня к некоторым ошибкам в моих рассуждениях о замыканиях. это заставило меня поверить, что переменные, объявленные в лексическое время (время автора), являются теми же самыми, к которым возвращаемая функция сохранила доступ (ссылку). Вот пример того, что я имею в виду.

function foo() {
   var baz = 0;
  
   function bar(){
      
     baz++;      
     return baz;
   }
   
   return bar;
}
var qux = foo();
var mur = foo(); 
qux(); // returns 1
mur(); // returns 1
// also returns 1, waaattt, why not 2, I thought mur                              retained a refrence to baz
qux(); //returns 2
mur(); // returns 2
// waaattt, so qux and mur are retaining a refrence to some sort of baz but waaattt baz

Это привело меня к логическому заключению, что qux и mur сохранили отсылку к baz, но не к одному и тому же baz. Это означает, что у qux и mur есть отдельный baz, на который они ссылаются. Хотя лексически вы бы так не подумали. TLDR; заключается в том, что лексическая область видимости — это не то же самое, что область выполнения.

Я думаю об этой разнице так: лексическую среду можно рассматривать как черновик или схему, а не как фактический конечный продукт. Среда выполнения, также известная как браузер (узел и т. д.), — это фабрика, производящая продукт на основе чертежей. Продукт можно рассматривать как контекст выполнения. Каждый раз, когда функция выполняется, среда выполнения создает новый контекст выполнения. Контекст имеет правила области действия, основанные на схеме. Итак, вернемся к приведенному выше примеру, когда запускаются qux и mur, выполняется foo и для каждого создается новый контекст выполнения. Каждый из этих контекстов содержит новые переменные, основанные на схеме. В каждом контексте есть своя версия baz и bar. Когда foo завершает работу, она возвращает новую версию bar, которая запоминает контекст выполнения, в котором она была создана. Таким образом, qux и mur — это отдельные версии bar, которые помнят контекст, в котором они были созданы. По сути, qux и mur являются замыканиями. Замыкание — это комбинация функции и контекста выполнения, в котором она была создана.

Таким образом, замыкания представляют собой комбинацию функции и среды, в которой она была создана. Среда относится к контексту выполнения, а не к лексическому контексту.