Чем let отличается от var
В JavaScript let и var используются для объявления переменных, но у них есть существенные различия в поведении. Эти различия касаются:
-
области видимости (scope),
-
поднятия (hoisting),
-
повторного объявления,
-
поведения внутри циклов и замыканий,
-
взаимодействия с глобальным объектом.
var — более старый способ, существующий с момента создания JavaScript. let был введён в стандарте ECMAScript 6 (ES6) в 2015 году как более безопасная и предсказуемая альтернатива.
1. Область видимости (Scope)
var — функциональная область видимости (function scope)
Переменные, объявленные через var, видимы во всей функции, в которой объявлены, вне зависимости от того, где именно внутри функции они написаны.
function example() {
if (true) {
var x = 10;
}
console.log(x); // 10 — переменная доступна вне блока
}
Если var используется вне функции, переменная становится глобальной.
let — блочная область видимости (block scope)
Переменные, объявленные через let, доступны только внутри блока, в котором они объявлены ({ ... }).
function example() {
if (true) {
let y = 20;
}
console.log(y); // ReferenceError: y is not defined
}
Это делает let более надёжным при работе с условиями, циклами, а также предотвращает утечки переменных.
2. Поднятие (Hoisting)
Hoisting — это механизм, при котором объявления переменных «поднимаются» в начало области видимости.
var поднимается с инициализацией undefined
console.log(a); // undefined
var a = 5;
Это эквивалентно:
var a;
console.log(a); // undefined
a = 5;
let поднимается, но остаётся в «временной мёртвой зоне» (TDZ)
console.log(b); // ReferenceError
let b = 10;
Хотя let тоже поднимается, доступ к переменной до её объявления невозможен. Это промежуток между началом блока и строкой с let, в котором переменная существует, но недоступна.
3. Повторное объявление
var можно объявить несколько раз в одной области видимости
var x = 1;
var x = 2; // допустимо, x перезаписан
let нельзя повторно объявить в одной области
let y = 1;
let y = 2; // SyntaxError: Identifier 'y' has already been declared
Это помогает избежать случайных переопределений переменных, особенно в больших функциях или модулях.
4. Циклы и замыкания
Одна из самых важных причин появления let — правильное поведение в циклах с замыканиями.
Пример с var — переменная общая для всех итераций
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Выведет: 3, 3, 3 — после окончания цикла i === 3
Все функции замыкаются на одну и ту же переменную i, которая к моменту вызова становится равной 3.
Пример с let — переменная уникальна для каждой итерации
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Выведет: 0, 1, 2
Здесь на каждую итерацию создаётся новая область видимости, и переменная i — отдельная для каждой.
5. Глобальные переменные и глобальный объект
var при объявлении в глобальной области становится свойством глобального объекта
В браузере глобальный объект — это window. Пример:
var x = 5;
console.log(window.x); // 5
let при объявлении глобально не добавляется к глобальному объекту
let y = 10;
console.log(window.y); // undefined
Это делает let более безопасным при написании кросс-платформенного кода, предотвращает нежелательные конфликты.
6. Удаление переменных
Переменные, объявленные через var в глобальной области, можно удалить с помощью delete.
var x = 10;
delete x; // true
А вот переменные, объявленные через let, удалить нельзя.
let y = 20;
delete y; // false
7. Переопределение переменной в глобальной области
var x = 1;
let x = 2; // SyntaxError: Identifier 'x' has already been declared
Если переменная уже объявлена через var, её нельзя повторно объявить через let в той же области.
8. Поведение в eval
eval выполняет строку как JavaScript-код. Переменные, объявленные через var внутри eval, становятся доступными вне его, если он вызван в глобальной области.
eval("var x = 1");
console.log(x); // 1
А вот переменные, объявленные через let, остаются внутри eval.
eval("let y = 2");
console.log(y); // ReferenceError
9. Поведение в with (устаревшее, но возможно)
var может создавать переменные, которые влияют на поведение блока with, что делает код менее предсказуемым.
with (obj) {
var x = 42;
}
let не позволяет такие конструкции — они запрещены синтаксически (SyntaxError).
10. Влияние на замыкания и асинхронный код
Особое поведение let в сочетании с функциями и асинхронностью позволяет избежать ошибок, связанных с общими ссылками на переменные.
Пример:
function createFunctionsVar() {
let arr = \[\];
for (var i = 0; i < 3; i++) {
arr.push(() => i);
}
return arr;
}
console.log(createFunctionsVar().map(fn => fn())); // \[3, 3, 3\]
function createFunctionsLet() {
let arr = \[\];
for (let i = 0; i < 3; i++) {
arr.push(() => i);
}
return arr;
}
console.log(createFunctionsLet().map(fn => fn())); // \[0, 1, 2\]
let создаёт отдельную переменную i на каждую итерацию цикла, из-за чего функции запоминают правильные значения.
11. Стандарты и стиль
Современные стандарты JavaScript (ES6+) и инструменты статического анализа (например, ESLint) рекомендуют не использовать var вовсе, отдавая предпочтение let и const.
let используется для переменных, значение которых должно изменяться.
Для переменных, которые не изменяются, рекомендуется использовать const.