var, let, const
var, let, const
До ES6 (2015) в JavaScript был только var. Появление let и const решило множество проблем, которые var создавал. Сегодня var в новом коде не используется.
var — функциональный скоуп
var ограничен функцией, а не блоком {}:
function example() {
if (true) {
var x = 10; // var видна во всей функции, не только в блоке if
}
console.log(x); // 10 — работает, хотя x объявлена внутри if
}
// for с var — классическая ловушка
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 3, 3, 3 — не 0, 1, 2!
}
Всплытие (hoisting) var
Объявления var поднимаются в начало функции, но не их значения:
console.log(a); // undefined — переменная существует, но значение ещё не присвоено
var a = 5;
console.log(a); // 5
let — блочный скоуп
let ограничен блоком {}:
if (true) {
let blockVar = 'видна только здесь';
}
console.log(blockVar); // ReferenceError: blockVar is not defined
// for с let — каждая итерация получает свою переменную
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 0, 1, 2 — правильно!
}
const — константа
const — блочный скоуп + нельзя переназначить:
const PI = 3.14159;
PI = 3; // TypeError: Assignment to constant variable
// Но объект или массив можно мутировать!
const user = { name: 'Иван' };
user.name = 'Мария'; // OK — мы меняем содержимое, не переменную
user = {}; // TypeError — нельзя переназначить саму переменную
Правило выбора: по умолчанию используйте
const. Переключайтесь на let только если значение переменной изменится. var не используйте никогда.Temporal Dead Zone (TDZ)
let и const существуют в TDZ — периоде до их объявления, когда обращение к ним вызывает ошибку:
console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 5;
// В отличие от var:
console.log(y); // undefined (не ошибка!)
var y = 5;
TDZ кажется ограничением, но это защита от багов. Классическая ошибка с var:
// Разработчик имел в виду проверку до объявления
function getUserName() {
if (user) { // undefined — не ошибка, просто falsy
return user.name;
}
var user = getCurrentUser();
return user.name;
}
С let та же ошибка сразу бросит ReferenceError — баг будет обнаружен немедленно, а не проявится как непонятное поведение в продакшне.