В главе о функциях JavaScript вы узнали, что в JavaScript область действия переменной может быть глобальной или локальной. Начиная с ES6, вы также можете создавать переменные в области блока, используя ключевое слово let
.
К глобальной переменной можно обращаться и манипулировать в любом месте программы, тогда как к локальной переменной можно обращаться и манипулировать только той функцией, в которой они объявлены.
Однако существуют определенные ситуации, когда вы хотите, чтобы переменная была доступна по всему скрипту, но вы не хотите, чтобы какая-либо часть вашего кода могла случайно изменить ее значение.
Давайте посмотрим, что произойдет, если вы попытаетесь достичь этого с помощью глобальной переменной:
// Глобальная переменная
var counter = 0;
// Функция, предназначенная для манипулирования переменной 'counter'
function makeCounter() {
return counter += 1;
}
// Вызов функции
makeCounter();
console.log(counter); // Выводит: 1
makeCounter();
console.log(counter); // Выводит: 2
// Попытка манипулировать переменной 'counter' извне
counter = 10;
console.log(counter); // Выводит: 10
Как видно из приведенного выше примера, значение переменной counter
можно изменить из любой точки программы, не вызывая функцию makeCounter()
.
Теперь давайте попробуем добиться того же с локальной переменной и посмотрим, что произойдет:
function makeCounter() {
// Локальная переменная
var counter = 0;
// Управление переменной 'counter'
return counter += 1;
}
// Вызов функции
console.log(makeCounter()); // Выводит: 1
console.log(makeCounter()); // Выводит: 1
В этом случае переменной counter
нельзя манипулировать извне, поскольку она является локальной для функции makeCounter()
, но ее значение также не будет увеличиваться после последующего вызова функции, поскольку каждый раз, когда мы вызываем функцию, она сбрасывает значение переменной counter
. «Закрытие» (closure) JavaScript может решить нашу проблему.
Закрытие — это в основном внутренняя функция, которая имеет доступ к области видимости родительской функции, даже после того, как родительская функция закончила выполнение. Это достигается созданием функции внутри другой функции. Давайте посмотрим на следующий пример, чтобы увидеть, как это работает:
function makeCounter() {
var counter = 0;
// Вложенная функция
function make() {
counter += 1;
return counter;
}
return make;
}
/* Выполняем функцию makeCounter() и сохраняем возвращаемое значение в переменной myCounter */
var myCounter = makeCounter();
console.log(myCounter()); // Выводит: 1
console.log(myCounter()); // Выводит: 2
Как видно из приведенного выше примера, внутренняя функция make()
возвращает переменную во внешнюю функцию makeCounter()
. Таким образом, значением myCounter
является значение функции make()
. В JavaScript функции могут быть назначены переменным, переданы в качестве аргументов другим функциям, могут быть вложены в другие функции и т.д.
Вы также заметите, что внутренняя функция make()
все еще может получить доступ к значению переменной counter
, определенной во внешней функции, даже если функция makeCounter()
уже завершила выполнение. Это происходит потому, что замыкающие функции в JavaScript хранят ссылки на их внешние переменные и могут получать доступ и обновлять их значения.
В приведенном выше примере функция make()
является замыкающей; ее код ссылается на counter
внешней переменной, а это подразумевает, что всякий раз, когда вызывается функция make()
, код внутри нее может обращаться к переменной counter
и обновлять ее.
Наконец, поскольку внешняя функция завершила выполнение, никакая другая часть кода не может получить доступ к переменной counter
или манипулировать ею. Только внутренняя функция имеет доступ к этой переменной.
Предыдущий пример также может быть написан с использованием выражения анонимной функции, например:
// Выражение анонимной функции
var myCounter = (function() {
var counter = 0;
// Вложенная анонимная функция
return function() {
counter += 1;
return counter;
}
})();
console.log(myCounter()); // Выводит: 1
console.log(myCounter()); // Выводит: 2
В JavaScript все функции имеют доступ к глобальной области видимости, а также к области над ними. Поскольку JavaScript поддерживает вложенные функции, это означает, что вложенные функции имеют доступ к любому значению, объявленному в области действия родительской функции.
Локальные переменные имеют короткий жизненный цикл, они создаются при вызове функции и уничтожаются после ее завершения. Глобальные переменные существуют до тех пор, пока исполняется код на вашей веб-странице.
Создание функций Getter и Setter
Здесь мы создадим переменную secret
и защитим ее от прямого манипулирования извне кода с помощью замыкания. Мы также создадим функции получения и установки для получения и установки его значения.
Кроме того, функция setter
также выполнит быструю проверку, является ли указанное значение числом или нет, и если это не так, оно не изменит значение переменной.
var getValue, setValue;
// Самоисполняющаяся функция
(function() {
var secret = 0;
// Функция Getter
getValue = function() {
return secret;
};
// Функция Setter
setValue = function(x) {
if(typeof x === "number") {
secret = x;
}
};
}());
// Вызов функций
getValue(); // Возвращает: 0
setValue(10);
getValue(); // Возвращает: 10
setValue(null);
getValue(); // Возвращает: 10
Самоисполняющиеся функции также называются выражением немедленного вызова функции (IIFE), незамедлительно выполняемой функцией или самовыполняющейся анонимной функцией.