Avatar ·

В чем разница между let и var в JavaScript?

Всем привет! Я только начал изучать JavaScript и столкнулся с непонятной для меня ситуацией. Когда я объявляю переменные через `var` и через `let`, код ведет себя по-разному, и я не могу понять почему. Вот пример, который меня смутил: ```javascript function test() { if (true) { var x = 10; let y = 20; } console.log(x); // 10 console.log(y); // ReferenceError: y is not defined } ``` Почему `x` доступен за пределами блока `if`, а `y` нет? Я думал, что обе переменные объявлены внутри одного и того же места. Еще один пример с циклом: ```javascript for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1000); } // Выводит: 3 3 3 for (let j = 0; j < 3; j++) { setTimeout(() => console.log(j), 1000); } // Выводит: 0 1 2 ``` Почему `var` в цикле ведет себя так странно? Я ожидал, что оба варианта выведут 0, 1, 2. Что я делаю не так? В чем принципиальная разница между этими двумя способами объявления переменных? Буду благодарен за подробное объяснение с примерами! <<>>

Основные различия между let и var

Ключевая разница между let и var заключается в области видимости (scope) и всплытии (hoisting). Давайте разберем каждый аспект подробно.

1. Область видимости (Scope)

var имеет функциональную область видимости — переменная видна во всей функции, независимо от того, в каком блоке она объявлена. let (и const) имеет блочную область видимости — переменная видна только внутри блока {}, где объявлена.

function example() {
  if (true) {
    var a = 1;
    let b = 2;
  }
  console.log(a); // 1 — var "вылезает" за блок if
  console.log(b); // ReferenceError — let живет только внутри if
}

// Другой пример с блоками:
{
  var c = 3;
  let d = 4;
}
console.log(c); // 3
console.log(d); // ReferenceError

2. Всплытие (Hoisting)

Обе конструкции "всплывают" (hoisting), но по-разному:

  • var: объявление всплывает, инициализируется как undefined — можно использовать до объявления
  • let: объявление всплывает, но НЕ инициализируется — находится во "временной мертвой зоне" (Temporal Dead Zone, TDZ) до строки объявления
console.log(x); // undefined — var всплыл и инициализирован
var x = 5;

console.log(y); // ReferenceError — let в TDZ
let y = 10;

3. Повторное объявление

var позволяет переобъявлять одну и ту же переменную, let — нет (в той же области видимости):

var a = 1;
var a = 2; // OK — переопределили

let b = 1;
let b = 2; // SyntaxError: Identifier 'b' has already been declared

4. Проблема с циклами и setTimeout (ваш второй пример)

Это классическая проблема замыканий (closures). Объясним на примере:

// С var:
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Вывод: 3, 3, 3
// Почему? var i имеет функциональную область видимости.
// К моменту выполнения setTimeout, цикл уже закончился и i = 3.
// Все три колбэка ссылаются на одну и ту же переменную i.

// С let:
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 100);
}
// Вывод: 0, 1, 2
// Почему? let создает новую переменную j для каждой итерации цикла.
// Каждый колбэк "запоминает" свое значение j.

5. Глобальная область видимости

На глобальном уровне var создает свойство объекта window (в браузере), а let — нет:

var globalVar = 'я видна в window';
let globalLet = 'я не видна в window';

console.log(window.globalVar); // 'я видна в window'
console.log(window.globalLet); // undefined

Практические рекомендации

  • Всегда используйте let (или const) вместо var в современном JavaScript
  • const — для переменных, которые не должны переназначаться (по умолчанию)
  • let — когда нужно переопределить переменную
  • var — избегайте, если только не поддерживаете очень старые браузеры (IE10 и ниже)

Запомните простое правило: let — это современная, безопасная замена var, которая ведет себя более предсказуемо и соответствует ожиданиям большинства разработчиков.

<<>>

Дополнительный взгляд: let vs var через призму best practices

Коллеги уже подробно описали технические различия. Я хочу добавить практическую перспективу: почему сообщество JavaScript перешло на let/const.

Проблемы с var исторически приводили к трудноуловимым багам. Например:

// Классическая ошибка с var в циклах
var buttons = document.querySelectorAll('button');
for (var i = 0; i < buttons.length; i++) {
  buttons[i].onclick = function() {
    console.log('Нажата кнопка ' + i); // Всегда выводит последнее значение i
  };
}

// Исправление с let
for (let i = 0; i < buttons.length; i++) {
  buttons[i].onclick = function() {
    console.log('Нажата кнопка ' + i); // Каждая кнопка выводит свой номер
  };
}

Еще один важный момент — читаемость кода. Когда вы видите let или const, вы сразу знаете границы жизни переменной. var же может "вылезти" из любого блока, что усложняет понимание кода.

Современные линтеры (ESLint) и TypeScript по умолчанию запрещают var в новых проектах. Это не просто мода — это осознанный выбор в пользу более безопасного и предсказуемого кода.

Мой совет: забудьте про var, используйте const по умолчанию, let — когда нужно переназначение. Ваш код станет чище, а багов — меньше.