Переменные в JavaScript: var, let, const

Содержание

О проверке кода. Примеры не запускались — в отличие от материалов по Node.js с фактическим выводом. Говорим прямо, а не ставим бейдж «проверено».

Переменные — первое, что учат в JavaScript, и первое, где старые туториалы дают устаревший совет. Разберём три способа объявить переменную и когда какой.

Три ключевых слова

var a = 1;     // старый способ, всплывает, виден во всей функции
let b = 2;     // блочная область, можно переприсвоить
const c = 3;   // блочная область, переприсвоить нельзя

Правило 2026 года короткое: const по умолчанию, let когда значение меняется, var — не используйте в новом коде.

Почему const по умолчанию, а не let? Потому что большинство переменных на самом деле не меняются, а const делает это явным: читатель сразу видит, что значение постоянно, и защищён от случайного переприсваивания.

Область видимости: главное отличие

function example() {
  if (true) {
    var x = 1;    // виден во ВСЕЙ функции
    let y = 2;    // виден только в этом блоке { }
  }
  console.log(x);   // 1 — работает
  console.log(y);   // ReferenceError — y не существует здесь
}

var игнорирует блоки — он виден во всей функции, где объявлен. let и const живут в своём блоке {} — в if, в цикле, в любых фигурных скобках.

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

// ❌ var: все три колбэка напечатают 3
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i));   // 3, 3, 3
}

// ✅ let: новая переменная на каждой итерации
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i));   // 0, 1, 2
}

С var переменная i — одна на весь цикл, и к моменту выполнения колбэков она уже равна 3. С let на каждой итерации создаётся новая i, и каждый колбэк запоминает своё значение. До появления let это лечили замыканиями и IIFE.

Hoisting: почему код видит переменную «заранее»

console.log(a);   // undefined — не ошибка!
var a = 5;

console.log(b);   // ReferenceError — «мёртвая зона»
let b = 5;

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

Всплытие: var против let и const

var всплывает со значением

Доступен до объявления

  • Объявление поднимается наверх
  • Значение — undefined до присваивания
  • Обращение до строки — не ошибка, а undefined
  • Источник скрытых багов

let / const — мёртвая зона

Ошибка до объявления

  • Тоже всплывают, но без значения
  • Обращение до строки → ReferenceError
  • «Temporal Dead Zone»
  • Ошибка честнее молчаливого undefined

Поведение let здесь лучше: обращение к переменной до её объявления — это почти всегда ошибка, и ReferenceError честнее, чем молчаливый undefined, который потом всплывёт непонятным багом.

const не значит «неизменяемый»

Частое заблуждение:

const user = { name: 'Аня' };
user.name = 'Борис';    // ✅ работает — меняем СВОЙСТВО
user.age = 30;          // ✅ работает — добавляем свойство

user = {};              // ❌ TypeError — переприсваиваем ПЕРЕМЕННУЮ

const защищает саму переменную, а не значение под ней. Переприсвоить user другому объекту нельзя, но менять содержимое объекта — можно сколько угодно. То же с массивами: const arr = [] не мешает arr.push(1).

Если нужна настоящая неизменяемость объекта — Object.freeze:

const config = Object.freeze({ port: 3000 });
config.port = 8080;     // молча не сработает (в строгом режиме — ошибка)

Никогда без ключевого слова

function bad() {
  leak = 5;   // ❌ создаёт ГЛОБАЛЬНУЮ переменную!
}

Присваивание без const, let или var создаёт переменную в глобальной области — она видна отовсюду и живёт до конца программы. Это классический источник трудноуловимых багов, когда две функции неожиданно делят одну переменную. В строгом режиме такое просто запрещено и даёт ошибку — ещё одна причина, по которой модули (всегда строгие) безопаснее.

Правила именования

Короткие практические договорённости:

  • camelCase для переменных и функций: userName, getData;
  • UPPER_CASE для настоящих констант-значений: MAX_SIZE, API_URL;
  • осмысленно, но не длинно: user лучше, чем u и чем theUserObjectFromDatabase;
  • начинать можно с буквы, _ или $; не с цифры; ключевые слова — нельзя.

Смежные темы: типы данных — что кладём в переменные; область видимости и hoisting — подробнее; ключевые слова — что нельзя называть. Полный список — в уроках JavaScript.

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

Что использовать — var, let или const?
По умолчанию const, let — если значение переменной меняется, var — никогда в новом коде. const и let живут в блоке и не всплывают, что убирает целый класс ошибок var.
const делает значение неизменяемым?
Нет. const запрещает переприсвоить саму переменную, но объект или массив под ним менять можно: добавлять поля, элементы. Для настоящей неизменяемости объекта нужен Object.freeze.
Что такое hoisting?
Всплытие: объявления переменных и функций как будто поднимаются в начало области видимости. var всплывает со значением undefined, let и const — в «мёртвую зону», где обращение до объявления даёт ошибку.
Чем область видимости let отличается от var?
var виден во всей функции, let — только в своём блоке {}. Поэтому let в цикле создаёт новую переменную на каждой итерации, а var — одну на всех, из-за чего колбэки получали последнее значение.
Можно ли объявить переменную без ключевого слова?
Технически да, но это создаёт глобальную переменную и в строгом режиме запрещено. Всегда объявляйте через const или let — иначе переменная утечёт в глобальную область.