В предыдущей статье рассматривался порядок выполнения кода. Следующий важный шаг — понять, где и когда доступны переменные. Большинство ошибок с undefined, переопределениями и «почему тут работает, а там нет» связаны именно с областями видимости.

Начнём с простого примера.

Переменная и место её объявления

const message = 'Hello';

console.log(message);

Здесь всё ожидаемо: переменная объявлена и сразу доступна.

Теперь добавим блок кода.

if (true) {
  const text = 'Inside';
}

console.log(text);

Результат:

ReferenceError: text is not defined

Почему так произошло?

Блочная область видимости

Любой код внутри фигурных скобок {} создаёт блок:

  • if
  • for
  • while
  • try / catch
  • просто {}

Переменные, объявленные с помощью let и const, видны только внутри этого блока.

if (true) {
  const value = 10;
  console.log(value); // 10
}

console.log(value); // ошибка

Это поведение называется блочной областью видимости.

Зачем это вообще нужно

Рассмотрим пример без блоков:

let result = 0;

for (let i = 0; i < 3; i++) {
  result += i;
}

console.log(result); // 3
console.log(i);      // ошибка

Переменная i:

  • нужна только внутри цикла
  • не «засоряет» внешний код
  • не может быть случайно использована позже

Это делает код более безопасным и предсказуемым.

Чем отличается var

Теперь рассмотрим var.

if (true) {
  var count = 5;
}

console.log(count); // 5

Переменная count доступна вне блока, хотя объявлена внутри if.

Причина в том, что var не имеет блочной области видимости. Она ограничена только функцией.

function test() {
  var a = 1;
}

console.log(a); // ошибка

Здесь a недоступна, потому что функция создаёт собственную область видимости.

Почему var считается проблемным

Пример:

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

Ожидаемый результат для многих:

0
1
2

Фактический результат:

3
3
3

Причина:

  • var i существует в одной области видимости
  • к моменту выполнения таймера цикл уже завершён
  • значение i равно 3

Тот же код с let

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

Результат:

0
1
2

let создаёт новую переменную для каждой итерации, что делает поведение логичным.

const — это не про неизменяемость объекта

const user = {
  name: 'Alex'
};

user.name = 'John'; // допустимо

const означает:

  • нельзя переопределить саму переменную
  • можно менять содержимое объекта

Ошибка будет только здесь:

user = {}; // TypeError

Когда использовать let, а когда const

Практическое правило:

  • const — по умолчанию
  • let — если значение действительно меняется
  • var — не использовать

Пример:

const users = [];
let currentIndex = 0;

Поднятие объявлений (без углубления)

JavaScript «знает» о переменных до выполнения кода, но:

console.log(a);
let a = 5;

Результат:

ReferenceError

Для сравнения:

console.log(b);
var b = 5;

Результат:

undefined

Это ещё одна причина, почему var ведёт к ошибкам, которые сложно отлаживать.

Практический вывод

На этом этапе достаточно понять:

  1. let и const имеют блочную область видимости
  2. var — только функциональную
  3. const не делает объекты неизменяемыми
  4. Использование let и const делает код безопаснее

В следующей статье будет разобрано, как области видимости связаны с замыканиями и почему это один из самых полезных механизмов JavaScript.