Замыкания и области видимости — это темы, которые часто кажутся новичкам магией. На самом деле всё логично и даже элегантно. Эта статья поможет вам разобраться, как JavaScript управляет переменными и почему замыкания — мощный инструмент, а не страшный зверь.
Что такое область видимости и с чем её едят
Область видимости (scope) — это правила, по которым JavaScript решает, где переменная доступна. Представьте, что переменные живут в комнатах. В своей комнате вы видите всё, что в ней есть. Но из коридора не видно, что лежит в закрытой комнате. Так же и с переменными: одни доступны везде, другие — только в определённом блоке кода.
В JavaScript есть несколько типов областей видимости:
- Глобальная — переменная доступна из любого места программы.
- Функциональная — переменная видна только внутри функции, где объявлена.
- Блочная — переменная существует только внутри блока
{}(появилась с let и const).
До ES6 в JavaScript не было блочной области видимости для переменных, объявленных через var. Они «поднимались» (hoisting) и могли утекать за пределы блока. Сейчас же let и const работают более предсказуемо.
// Пример: var vs let в блоке
if (true) {
var a = 1;
let b = 2;
}
console.log(a); // 1 — var «просачивается» наружу
console.log(b); // ReferenceError: b is not defined
Понимание областей видимости помогает избегать случайных перезаписей переменных и багов, связанных с областью действия.
Замыкания: что это и как работают
Замыкание (closure) — это функция, которая запоминает своё лексическое окружение, даже когда вызывается вне этого окружения. Проще говоря, функция «видит» переменные из той области, где была создана, даже если выполняется совсем в другом месте.
Вот классический пример:
function makeCounter() {
let count = 0;
return function() {
count += 1;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
Функция, возвращаемая из makeCounter, сохраняет доступ к переменной count даже после того, как makeCounter завершила выполнение. Это и есть замыкание: внутренняя функция «замыкает» в себе переменную из внешней функции.
Замыкания часто используются для создания приватных переменных, каррирования, обработчиков событий и многого другого.
Лексическое окружение и цепочка областей видимости
Каждая функция в JavaScript имеет связанное с ней лексическое окружение (lexical environment). Это внутренняя структура, которая хранит переменные и ссылку на внешнее окружение. Когда функция ищет переменную, она сначала смотрит в своё окружение, затем во внешнее, затем во внешнее внешнего и так далее — до глобального. Эта последовательность называется цепочкой областей видимости (scope chain).
Рассмотрим пример:
const globalVar = 'global';
function outer() {
const outerVar = 'outer';
function inner() {
const innerVar = 'inner';
console.log(innerVar); // 'inner'
console.log(outerVar); // 'outer'
console.log(globalVar); // 'global'
}
inner();
}
outer();
Функция inner имеет доступ к своим переменным, затем к переменным outer, затем к глобальным. Если переменная не найдена ни в одном из окружений, возникает ошибка ReferenceError.
Цепочка областей видимости определяется в момент создания функции и не меняется. Это ключевое отличие от динамической области видимости, которая была бы основана на стеке вызовов.
Практические примеры и лайфхаки
Замыкания могут быть не только теоретической концепцией, но и полезным инструментом в повседневной разработке. Вот несколько сценариев, где они пригодятся.
Приватность данных
С помощью замыканий можно эмулировать приватные свойства объектов, недоступные извне.
function createPerson(name) {
let _name = name;
return {
getName() {
return _name;
},
setName(newName) {
_name = newName;
}
};
}
const person = createPerson('Alice');
console.log(person.getName()); // 'Alice'
person.setName('Bob');
console.log(person.getName()); // 'Bob'
console.log(person._name); // undefined — приватная переменная
Обработчики событий с сохранением контекста
Замыкания помогают сохранять нужные значения в асинхронных вызовах.
for (let i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i); // 1, 2, 3 — благодаря let и замыканию
}, i * 1000);
}
Если бы мы использовали var, то все колбэки вывели бы 4. С let каждая итерация создаёт новую переменную i, и замыкание фиксирует её значение.
Сравнение областей видимости: таблица
Для наглядности сведём основные типы областей видимости в таблицу.
| Тип области видимости | Ключевые слова | Доступность |
|---|---|---|
| Глобальная | var, let, const (вне функций) | Везде |
| Функциональная | var, let, const (внутри функции) | Только внутри функции |
| Блочная | let, const (внутри {}) | Только внутри блока |
| Модульная | Переменные модуля | Внутри модуля (если не экспортированы) |
Эта таблица помогает быстро вспомнить, где какая переменная будет видна.
Что в итоге
Области видимости и замыкания — фундаментальные концепции JavaScript. Они определяют, как переменные доступны и как функции запоминают своё окружение. Понимание этих механизмов позволяет писать более надёжный, чистый и эффективный код.
Замыкания — это не сложно, если помнить, что функция запоминает место своего рождения. Используйте их для инкапсуляции, создания функций с состоянием и избегания глобального загрязнения. Экспериментируйте, и вы увидите, насколько мощным может быть этот инструмент.
Студия WNDER



