Неудивительно, что, когда вы впервые начинаете изучать JavaScript, замыкания кажутся довольно пугающими. Но на самом деле они действительно простые - давайте посмотрим, как это сделать.

function outer(){
  var a="From outer "
    return function inner(){
       var b="to inner"
      console.log(a+b);
    }
}
//outer();
//outer()();
//inner();

Если вы знакомы с функциями, замыкания - это не что иное, как функции, которые используют переменные во внешних функциях, которые были ранее возвращены. Для начала скопируйте приведенный выше код и запустите его в консоли. Как вы думаете, что будет в результате вызова соответствующих функций?

Когда мы вызвали outer(), мы получили внутреннюю функцию как есть. Когда мы вызвали outer()(), у нас на консоли конкатенировались две строки. Когда мы вызывали inner(), мы получили ошибку.

Давайте посмотрим на другой пример:

function outer(){
 var a=10;
   return function inner(){
     console.log(a*10);
    }
}
outer()();

Вам может показаться странным вызывать функции с помощью ()() . Но именно это синтаксически существенно отличает замыкания от вложенных функций.

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

Допустим, вы хотите написать простую функцию для умножения двух чисел. Очевидный способ:

function multiply(a,b){
    return a*b;
}
multiply(3,5);

Попробуем другой способ.

function multiply(a){
   return function(b){
     return a*b;
   }
}
multiply(3)(5);

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

function add(a){
  function(b){
     return a+b;
  }
}
add(8)(7);

Если вы сначала запустите приведенный выше код, вы получите сообщение об ошибке «Операторы функции требуют имени функции», а затем еще одно сообщение «add (…) не является функцией». Это потому, что мы не вернули внутреннюю функцию. Следовательно, использование ()() для вызывающего вызова не имеет смысла. Если вы удалите одну пару (), она вернет «undefined», что и должно быть, поскольку вы на самом деле ничего не возвращаете.

Вот и все. Это вложенные функции, в которых внутренняя функция использует переменную внешней функции. И не забудьте добавить ключевое слово return перед внутренней функцией.

Давайте посмотрим на практическое использование замыканий

Замыкания можно использовать для создания частной переменной. Частная переменная не может быть изменена извне.

Давайте посмотрим на другой пример, чтобы понять это. Допустим, вы хотите подсчитать, сколько раз пользователь нажимал кнопку.

<button onclick="clickCount()">click me!</button>

Вы можете использовать глобальную переменную(cnt) для подсчета.

var cnt=0;
function clickCount(){
 cnt++;
}

Но здесь есть небольшая проблема: любой скрипт на странице может изменить значение нашей переменной cnt , даже не вызывая функцию clickCount().

На самом деле это не проблема - давайте просто объявим cnt внутри функции.

function clickCount(){
var cnt=0;
cnt++;
}

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

function cntWrapper() {
    var cnt = 0;
    function clickCount() {
    cnt++;
    }
    clickCount();    
    return cnt; 
}

Помните, что вложенные функции имеют доступ к области над ними. Чтобы вызвать внутреннюю функцию, вам нужно будет вызвать внешнюю функцию, и снова ваша переменная счетчика будет каждый раз начинаться с 0. Похоже, мы вернулись на круги своя. Если бы только мы могли вызывать внутреннюю функцию извне.

Введите закрытие (каламбур)

var updateCnt=(function() {
    var cnt = 0;
    return function() {
    cnt++;
    return cnt;
}
})();

Мы создали самозапускающуюся функцию, которая запускается только один раз, инициализирует cnt значением 0 и возвращает выражение функции.

Наша привязка функции может получить доступ к cnt в родительской области. Вы заметите, что каждый раз, когда вы вызываете updateCnt(), ваш счетчик принимает предыдущее значение и добавляет к нему 1. Если вы наберете cnt в консоли, вы получите сообщение об ошибке, что он не определен. Таким образом, наша переменная cnt, теперь полностью безопасная и надежная, по сути, является частной переменной, защищенной областью действия анонимной функции.

Это все, что вам нужно знать о закрытии!