Перейти к содержанию
Innovialabs

var, let и const в JavaScript: область видимости и hoisting

Основы3 мин

Коротко

Используйте const по умолчанию и let, когда переменную нужно переприсвоить; избегайте var — он виден во всей функции и поднимается со значением undefined, что порождает тонкие баги.

Короткий ответ

  • const — блочная область видимости, переприсвоить нельзя. Используйте по умолчанию.
  • let — блочная область видимости, переприсвоить можно. Для счётчиков, накопителей и значений, которые действительно меняются.
  • var — область видимости функции, поднимается со значением undefined, можно объявить повторно. В новом коде не используйте.
varletconst
Область видимостифункцияблокблок
Переприсвоениедаданет
Повторное объявлениеданетнет
До объявленияundefinedReferenceError (TDZ)ReferenceError (TDZ)
Обязательная инициализациянетнетда

Область видимости функции и блока

Блок — всё, что в фигурных скобках: if, for, while или просто { }. let и const живут только внутри блока. var блоки игнорирует и принадлежит всей функции (или глобальной области).

function demo() {
  if (true) {
    var a = 1;
    let b = 2;
  }
  console.log(a); // 1 — var «протёк» из блока if
  console.log(b); // ReferenceError: b is not defined
}

Эта утечка var — корень большинства проблем: переменная, которую вы считали локальной для цикла или условия, видна и изменяема во всей функции.

Hoisting и временная мёртвая зона

Hoisting (поднятие) означает, что движок регистрирует объявления до выполнения кода их области. Поднимаются все три ключевых слова, но по-разному:

  • var создаётся и сразу инициализируется значением undefined.
  • let и const создаются, но не инициализируются. Пока не выполнится строка с объявлением, переменная находится во временной мёртвой зоне (TDZ), и любое обращение бросает ReferenceError.
console.log(x); // undefined
var x = 5;

console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 5;

TDZ — это полезное поведение: чтение переменной до присвоения значения почти всегда ошибка, и let/const сообщают о ней громко, а не молча отдают undefined.

Объявления функций (function declaration) поднимаются целиком, вместе с телом, поэтому их можно вызвать выше определения. Функциональные выражения, присвоенные const, подчиняются правилам TDZ.

Классический баг с замыканием в цикле

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// 3, 3, 3

На всю функцию существует одна переменная i. Колбэки выполняются после окончания цикла, а к этому моменту i равно 3.

С let каждая итерация получает новую привязку, и каждый колбэк захватывает своё значение:

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// 0, 1, 2

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

const не замораживает объекты

const защищает привязку, а не значение. Присвоить переменной новый объект нельзя, но изменить объект, на который она указывает, можно.

const user = { name: "Aziz" };
user.name = "Dilnoza";  // OK — объект изменён
user = {};              // TypeError: Assignment to constant variable

const list = [1, 2];
list.push(3);           // OK

Если нужен неизменяемый объект, используйте Object.freeze() (поверхностная заморозка) или пишите код так, чтобы создавать новые объекты вместо изменения старых.

Простое правило

  1. Сначала пишите const.
  2. Меняйте на let, только когда переменную действительно нужно переприсвоить.
  3. Не используйте var в новом коде. Это можно закрепить линтером — например, правилами no-var и prefer-const в ESLint.

Код ещё и читается легче: видите let — значит, значение где-то ниже меняется.

Частые ошибки

  • Считать, что const делает массивы и объекты доступными только для чтения.
  • Использовать var в циклах с асинхронными колбэками.
  • Объявить let внутри блока и ожидать, что переменная будет видна после него.
  • Обращаться к переменной в начале функции, «потому что есть hoisting», — с let/const это ошибка.
  • Случайно повторно объявить var в длинной функции и затереть значение.

Частые вопросы

На практике значимой разницы нет. Выбирайте по смыслу и безопасности, а не по скорости.

Поможем с этим

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

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.