JS / Основы
Область видимости переменных в JavaScript: let, const, var и замыкания
Глобальная, модульная, функциональная и блочная область видимости, временная мёртвая зона, замыкания и частые ошибки.
Содержание статьи
Область видимости определяет, где идентификатор доступен в коде. Чем уже область переменной, тем меньше мест могут случайно изменить её значение и тем проще понимать программу.
В современном JavaScript по умолчанию используйте const, а let — когда привязку действительно нужно переназначить. var встречается в старом коде и отличается функциональной, а не блочной областью.
Глобальная и модульная область
Переменная классического скрипта верхнего уровня может стать частью глобального окружения. Это создаёт риск конфликтов между файлами.
ES-модуль имеет собственную верхнеуровневую область:
<script type="module" src="main.js"></script>// main.jsconst applicationName = 'Code4web';console.log(window.applicationName); // undefinedЭкспортируйте только то, что нужно другим модулям:
export function formatDate(date) { return new Intl.DateTimeFormat('ru-RU').format(date);}Функциональная область
Параметры и объявления внутри функции недоступны снаружи:
function calculateTotal(price, quantity) { const total = price * quantity; return total;}console.log(calculateTotal(500, 3)); // 1500console.log(total); // ReferenceErrorКаждый вызов получает собственные параметры и локальные значения.
Блочная область let и const
Блок создаётся фигурными скобками у if, for, while или просто {}:
if (user.isAdmin) { const message = 'Доступ разрешён'; console.log(message);}console.log(message); // ReferenceErrorПеременная цикла тоже ограничена блоком:
for (let index = 0; index < 3; index += 1) { console.log(index);}console.log(index); // ReferenceErrorЭто защищает вспомогательные значения от утечки в соседний код.
Чем отличается var
var игнорирует обычный блок и ограничивается функцией:
function example() { if (true) { var result = 42; } console.log(result); // 42}Кроме того, объявление var поднимается и до строки инициализации имеет значение undefined:
console.log(value); // undefinedvar value = 10;Это не значит, что присваивание переместилось вверх. Движок обрабатывает примерно такую структуру:
var value;console.log(value);value = 10;В новом коде let и const делают ошибку заметнее.
Временная мёртвая зона
Объявления let и const тоже известны движку с начала блока, но недоступны до строки инициализации:
console.log(theme); // ReferenceErrorconst theme = 'dark';Период от начала блока до объявления называют temporal dead zone. Он предотвращает чтение переменной до получения осмысленного значения.
const не делает объект неизменяемым
const запрещает переназначить привязку:
const settings = { theme: 'light' };settings.theme = 'dark'; // Допустимо.settings = {}; // TypeError.Если нужна поверхностная защита объекта, есть Object.freeze(), но вложенные объекты он автоматически не замораживает.
Лексическая область и замыкания
Внутренняя функция видит переменные места, где была создана, даже после завершения внешнего вызова:
function createCounter(initialValue = 0) { let value = initialValue; return function increment() { value += 1; return value; };}const counter = createCounter(10);console.log(counter()); // 11console.log(counter()); // 12Связь функции с окружающей лексической областью называется замыканием. Значение value остаётся приватным для возвращённой функции.
Замыкание в цикле
let создаёт отдельную привязку для каждой итерации:
const buttons = document.querySelectorAll('[data-index]');for (let index = 0; index < buttons.length; index += 1) { buttons[index].addEventListener('click', () => { console.log(index); });}С var все обработчики увидели бы итоговое значение общего index. Ещё яснее перебирать элементы напрямую:
for (const [index, button] of [...buttons].entries()) { button.addEventListener('click', () => console.log(index));}Затенение переменных
Внутренняя переменная может иметь то же имя:
const status = 'global';function showStatus() { const status = 'local'; console.log(status);}Это допустимо, но частое затенение усложняет чтение. Используйте имена, отражающие роль: requestStatus, accountStatus.
Практические правила
- подключайте проектный код как ES-модули;
- объявляйте значение как можно ближе к месту использования;
- выбирайте
const, если переназначение не требуется; - используйте
letдля счётчиков и изменяемого состояния; - не создавайте неявные глобальные переменные;
- избегайте
varв новом коде; - не храните большое состояние в замыкании без необходимости — оно остаётся в памяти, пока доступна функция.
Что запомнить
const и let имеют блочную область, var — функциональную. Модули защищают верхний уровень от глобальных конфликтов, а замыкания позволяют функциям помнить внешние значения. Чем локальнее состояние, тем легче сопровождать программу.