Замыкание JavaScript

Переменные JavaScript могут относиться к локальной или к глобальной области видимости.

Глобальные переменные можно при помощи замыкания сделать локальными.

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

Глобальные переменные

У функции есть доступ ко всем переменным, определенным внутри данной функции. Как здесь:


function myFunction() {
    var a = 4;
    return a * a;
} 

Но у функции также есть доступ и к переменным, определенным за ее пределами. Как здесь:


var a = 4;
function myFunction() {
    return a * a;
} 

В последнем примере a — это глобальная переменная.

В веб-странице глобальные переменные принадлежат глобальному объекту window.

Глобальные переменные могут использоваться (и изменяться) всеми скриптами на странице (и в окне браузера).

В первом примере a — это локальная переменная.

Локальные переменные могут использоваться только внутри функции, где они определяются. Они скрыты от других функций и других скриптов.

Глобальные и локальные переменные с одинаковыми именами — это разные переменные. Изменение значения одной не меняет значение другой.

Внимание! Переменные, определенные без ключевого слова var, всегда будут глобальными. Даже если они определены внутри функции.

Время жизни переменной

Глобальные переменные существуют пока существует ваше приложение (окно браузера/веб-страница).

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

"Дилемма счетчика"

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

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


var counter = 0;

function add() {
    counter += 1;
}

 add();
 add();
 add();

// теперь счетчик counter равен 3 

Счетчик counter должен изменяться только функцией add().

Проблема состоит в том, что любой скрипт на странице может изменять переменную counter, не вызывая функцию add().

Если же декларировать переменную counter внутри функции, то никто не сможет изменить ее не вызвав функцию add():


function add() {
    var counter = 0;
    counter += 1;
}

 add();
 add();
 add();

// теперь counter должен быть равен 3, но это не работает ! 

Но это не будет работать! Всякий раз, когда вызывается функция add(), переменная counter будет устанавливаться в 1.

Решить это может внутренняя функция JavaScript.

Вложенные функции JavaScript

У всех функций есть доступ к глобальной области видимости.

На деле это означает, что у всех функций JavaScript есть доступ к области видимости "над" ними.

JavaScript поддерживает вложенные функции. У вложенных функций тоже есть доступ к области видимости "над" ними.

В следующем примере внутренняя функция plus() имеет доступ к переменной counter, расположенной в родительской функции:


function add() {
    var counter = 0;
    function plus() { counter += 1; }
    plus();    
    return counter; 
} 

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

К тому же нам нужно найти способ выполнить counter = 0 один раз.

Нам нужно замыкание.

Замыкание JavaScript

Помните самовызываемые функции? Что делает такая функция?


var add = (function () {
    var counter = 0;
    return function () { return counter += 1; }
})();

add();
add();
add();

// теперь переменная counter равна 3 

Объяснение примера

Переменной add присваивается значение return самовызываемой функции.

Самовызываемая функция выполняется только один раз. Она устанавливает переменную counter в ноль (0) и возвращает результат вычислений вложенной функции.

При таком способе переменная add становится функцией. Здесь замечательно то, что она может получить доступ к переменной counter в родительской области видимости.

Это называется замыкание JavaScript. Это позволяет функции иметь "частные" переменные.

Переменная counter защищена областью видимости анонимной функции и может быть изменена только при помощи функции add.