Чем 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.