Особенности let: блочная видимость и временная мертвая зона

Раздел: JavaScript -> Основы JavaScript

Основные особенности let

Оператор let появился в ECMAScript 2015 для объявления переменных с блочной областью видимости. В отличие от var, переменная, объявленная через let, существует только внутри блока (фигурных скобок) и не всплывает за его пределы. Это помогает избежать многих распространённых ошибок, связанных с неожиданным переопределением и загрязнением глобальной области.

Основное преимущество - чёткое управление временем жизни переменной. Переменная не видна до объявления (временная мёртвая зона, TDZ) и не может быть повторно объявлена в одной области.

// Основной пример
let message = 'Привет';
if (true) {
    let message = 'Мир'; // новая переменная в блоке
    console.log(message); // 'Мир'
}
console.log(message); // 'Привет'

Js json (json в javascript)

Как объявить переменную с помощью let?

Объявление происходит с ключевым словом let, затем указывается имя переменной и, опционально, начальное значение.

let name = 'Анна';
let age;
console.log(age); // undefined

Js данные (данные в javascript)

Типичная ошибка - попытка повторного объявления той же переменной в той же области видимости:

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

Js значение (значения в javascript)

В отличие от var, let не допускает повторных объявлений. Решение - использовать разные имена или переопределять без ключевого слова let.

Как работает область видимости let внутри блока?

Переменная, объявленная с let внутри фигурных скобок, не доступна снаружи. Это касается всех блоков: if, for, while, switch и просто составного оператора.

{
    let x = 10;
    console.log(x); // 10
}
console.log(x); // ReferenceError: x is not defined

Javascript let (оператор let в javascript)

Ошибка: попытка обратиться к переменной за пределами её блока. Решение: объявлять переменные в той области, где они нужны, или возвращать значение наружу.

Как let ведёт себя в циклах (for)?

В цикле for с let на каждой итерации создаётся новая переменная, даже если в условии используется одна и та же переменная итератора. Это особенно важно при использовании замыканий.

for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100);
}
// Результат: 0, 1, 2 (а не 3, 3, 3 как с var)

Js code (код на javascript)

Если бы использовался var, все обработчики вывели бы одно значение. let решает проблему без создания дополнительных замыканий.

Что такое временная мёртвая зона (TDZ)?

Переменная, объявленная с let, недоступна до момента её фактического объявления. Попытка обращения до инициализации вызывает ReferenceError.

console.log(value); // ReferenceError: Cannot access 'value' before initialization
let value = 5;

TDZ распространяется на весь блок до строки объявления. Это отличает let от var, который всплывает как undefined. Решение - всегда объявлять переменные в начале блока.

Чем let отличается от const?

И let, и const имеют блочную видимость и TDZ. Отличие в том, что const требует обязательной инициализации и запрещает переназначение, но не делает объект неизменяемым.

let a = 10;
a = 20; // OK
const b = 10;
b = 20; // TypeError: Assignment to constant variable

Ошибка: попытка изменить константу. Выбирайте let, если переменная будет переназначаться, и const для неизменяемых ссылок.

Расширенные примеры использования let

Ниже приведены неочевидные сценарии, где let раскрывает свои возможности.

let в асинхронных замыканиях циклов

Создание обработчиков событий внутри цикла - классическая проблема, решаемая с помощью let.

Пример
// С var: все кнопки выводят 'Item 3'
var buttonsVar = document.querySelectorAll('button');
for (var i = 0; i < buttonsVar.length; i++) {
    buttonsVar[i].onclick = function() {
        console.log('Item ' + i);
    };
}

// С let: каждая кнопка выводит свой индекс
let buttonsLet = document.querySelectorAll('button');
for (let j = 0; j < buttonsLet.length; j++) {
    buttonsLet[j].onclick = function() {
        console.log('Item ' + j);
    };
}
Результат: для var - 'Item 3' для всех кнопок; для let - 'Item 0', 'Item 1', 'Item 2' соответственно.

let с деструктуризацией и блочной областью

Переменные, созданные при деструктуризации через let, подчиняются блочной видимости.

Пример
{
    let [x, y] = [10, 20];
    console.log(x, y); // 10 20
}
console.log(x); // ReferenceError: x is not defined

Вложенные блоки и временная мёртвая зона

TDZ распространяется на весь блок, даже если есть вложенные области.

Пример
function test() {
    console.log(outer); // ReferenceError
    let outer = 1;
    {
        console.log(outer); // ReferenceError (из-за let inner в этом же блоке? Нет, outer объявлен выше, но inner не влияет)
        let inner = 2;
    }
}
В первой строке TDZ для outer, во второй - TDZ для outer не при чём, но если бы переменная inner была вызвана до объявления, то получили бы ReferenceError для inner.

let в switch и case

Переменные, объявленные с let внутри блоков case, изолированы.

Пример
let value = 2;
switch (value) {
    case 1: {
        let result = 'один';
        console.log(result);
        break;
    }
    case 2: {
        let result = 'два';
        console.log(result); // 'два'
        break;
    }
}
'два'

Использование let с метками

Метки позволяют выйти из внешнего блока, и переменные let не выходят за пределы своего блока.

Пример
outer: {
    let x = 10;
    inner: {
        let x = 20;
        console.log(x); // 20
        break outer;
    }
    console.log(x); // Не выполнится, так как break вывел из блока
}

let и повторное объявление в разных блоках

В разных блоках можно объявлять переменные с одним именем, это разные переменные.

Пример
let a = 1;
if (true) {
    let a = 2;
    console.log(a); // 2
}
console.log(a); // 1
2 и 1

let и глобальный объект (window)

В отличие от var, переменные let, объявленные в глобальной области, не становятся свойствами window.

Пример
let globalLet = 'не свойство';
var globalVar = 'свойство';
console.log(window.globalLet); // undefined
console.log(window.globalVar); // 'свойство'
undefined и 'свойство'

Оператор let в JavaScript - comments

En
Javascript let (javascript)