Особенности let: блочная видимость и временная мертвая зона
Основные особенности 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); // undefinedJs данные (данные в 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 definedJavascript 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); // 12 и 1
let и глобальный объект (window)
В отличие от var, переменные let, объявленные в глобальной области, не становятся свойствами window.
let globalLet = 'не свойство';
var globalVar = 'свойство';
console.log(window.globalLet); // undefined
console.log(window.globalVar); // 'свойство'undefined и 'свойство'