Перейти к содержимому

var vs let vs const

В одной фразе: var — function-scoped, hoisted (undefined), re-declarable; let — block-scoped, TDZ, не re-declarable; const — block-scoped, TDZ, immutable binding (но не значение). Классический вопрос на собесе — объяснить замыкание в цикле с var и setTimeout.

// Классическая ловушка: var в цикле + setTimeout
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // 3, 3, 3 — одна переменная i!
}
// let создаёт новый binding на каждой итерации
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 0); // 0, 1, 2 — три разных j
}
// var: function-scoped, игнорирует блочные скобки
function demo() {
if (true) {
var x = 'leak'; // x видна во всей функции
let y = 'block'; // y видна только в этом if-блоке
}
console.log(x); // 'leak' — var проигнорировала if-блок
// console.log(y); // ReferenceError
}
// var допускает повторное объявление без ошибки:
var a = 1;
var a = 2; // OK — молча переопределяет
let b = 1;
// let b = 2; // SyntaxError
// const: immutable binding, но не immutable значение
const obj = { count: 0 };
obj.count++; // OK — мутируем содержимое
// obj = {}; // TypeError: Assignment to constant variable
const arr = [1, 2, 3];
arr.push(4); // OK
// arr = []; // TypeError
// Для настоящей immutability:
const frozen = Object.freeze({ x: 1, nested: { y: 2 } });
frozen.x = 99; // silently fails (strict: TypeError)
frozen.nested.y = 99; // РАБОТАЕТ — freeze поверхностный!
console.log(frozen.x); // 1
console.log(frozen.nested.y); // 99

Итог: Правило: const по умолчанию, let если нужно переприсваивание, var — никогда в новом коде. const не означает immutable, только non-reassignable.