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 в цикле + setTimeoutfor (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); // 1console.log(frozen.nested.y); // 99Итог: Правило: const по умолчанию, let если нужно переприсваивание, var — никогда в новом коде. const не означает immutable, только non-reassignable.