let
Baseline Широко доступно
Эта функция хорошо зарекомендовала себя и работает на многих устройствах и версиях браузеров. Она доступна в браузерах с сентября 2016 года.
Объявление let объявляет локальные переменные с блочной областью видимости, которые можно переназначать, опционально инициализируя каждую значением.
Попробуйте
let x = 1;
if (x === 1) {
let x = 2;
console.log(x);
// Expected output: 2
}
console.log(x);
// Expected output: 1
Синтаксис
let name1; let name1 = value1; let name1 = value1, name2 = value2; let name1, name2 = value2; let name1 = value1, name2, /* …, */ nameN = valueN;
Параметры
-
nameN - Имя переменной для объявления. Каждое имя должно быть допустимым JavaScript идентификатором или шаблоном деструктурирующего присваивания.
-
valueNНеобязательно - Начальное значение переменной. Может быть любым допустимым выражением. Значение по умолчанию —
undefined.
Описание
Область видимости переменной, объявленной с помощью let, — это один из следующих синтаксисов, заключенных в фигурные скобки, который наиболее тесно содержит объявление let:
- Блочное выражение
-
switchвыражение -
try...catchвыражение - Тело одного из
forвыражений, еслиletнаходится в заголовке выражения - Тело функции
- Блок статической инициализации
Или, если ни одно из вышеперечисленных не применимо:
- Текущий модуль, для кода, выполняющегося в режиме модуля
- Глобальная область видимости, для кода, выполняющегося в режиме скрипта.
По сравнению с var, объявления let имеют следующие отличия:
-
Объявления
letимеют область видимости как блоков, так и функций. -
К объявлениям
letможно получить доступ только после достижения места объявления (см. временную мертвую зону). По этой причине объявленияletобычно считаются не поднимаемыми. -
Объявления
letне создают свойства вglobalThisпри объявлении на верхнем уровне скрипта. -
Объявления
letне могут быть переобъявлены любым другим объявлением в той же области видимости. -
letначинаются с объявлений, а не выражений. Это означает, что вы не можете использовать одиночное объявлениеletв качестве тела блока (что имеет смысл, поскольку нет способа получить доступ к переменной).if (true) let a = 1; // SyntaxError: Lexical declaration cannot appear in a single-statement context
Обратите внимание, что let разрешен в качестве имени идентификатора при объявлении с помощью var или function в нестрогом режиме, но вам следует избегать использования let в качестве имени идентификатора, чтобы предотвратить неожиданные синтаксические неоднозначности.
Многие руководства по стилю (включая MDN) рекомендуют использовать const вместо let всякий раз, когда переменная не переназначается в своей области видимости. Это четко показывает намерение, что тип переменной (или значение, в случае примитива) никогда не может измениться. Другие могут предпочесть let для не-примитивов, которые мутируют.
Список, следующий за ключевым словом let, называется списком привязки и разделяется запятыми, где запятые не являются операторами запятой, а знаки = не являются операторами присваивания. Инициализаторы последующих переменных могут ссылаться на предыдущие переменные в списке.
Временная мертвая зона (TDZ)
Переменная, объявленная с помощью let, const или class, находится во "временной мертвой зоне" (TDZ) с начала блока до тех пор, пока выполнение кода не достигнет места, где переменная объявлена и инициализирована.
Находясь в TDZ, переменной не присвоено значение, и любая попытка доступа к ней приведет к ReferenceError. Переменная инициализируется значением, когда выполнение достигает места в коде, где она была объявлена. Если начальное значение не было указано в объявлении переменной, оно будет инициализировано значением undefined.
Это отличается от переменных var, которые вернут значение undefined, если к ним обратиться до их объявления. Код ниже демонстрирует разный результат, когда к let и var обращаются в коде до места их объявления.
{
// TDZ starts at beginning of scope
console.log(bar); // "undefined"
console.log(foo); // ReferenceError: Cannot access 'foo' before initialization
var bar = 1;
let foo = 2; // End of TDZ (for foo)
}
Термин "временной" используется потому, что зона зависит от порядка выполнения (времени), а не от порядка написания кода (позиции). Например, следующий код работает, потому что, даже несмотря на то, что функция, использующая переменную let, появляется до объявления переменной, функция вызывается вне TDZ.
{
// TDZ starts at beginning of scope
const func = () => console.log(letVar); // OK
// Within the TDZ letVar access throws `ReferenceError`
let letVar = 3; // End of TDZ (for letVar)
func(); // Called outside TDZ!
}
Использование оператора typeof для переменной в ее TDZ приведет к ошибке ReferenceError:
{
typeof i; // ReferenceError: Cannot access 'i' before initialization
let i = 10;
}
Это отличается от использования typeof для необъявленных переменных и переменных, которые содержат значение undefined:
console.log(typeof undeclaredVariable); // "undefined"
Примечание: Объявления let и const обрабатываются только при обработке текущего скрипта. Если у вас есть два <script> элемента, выполняющихся в режиме скрипта, в одном HTML, первый скрипт не подвержен ограничениям TDZ для let или const переменных верхнего уровня, объявленных во втором скрипте, хотя, если вы объявите переменную let или const в первом скрипте, ее повторное объявление во втором скрипте вызовет ошибку переобъявления.
Переобъявления
Объявления let не могут находиться в той же области видимости, что и любое другое объявление, включая let, const, class, function, var и объявления import.
{
let foo;
let foo; // SyntaxError: Identifier 'foo' has already been declared
}
Объявление let внутри тела функции не может иметь того же имени, что и параметр. Объявление let внутри catch блока не может иметь того же имени, что и привязанный к catch идентификатор.
function foo(a) {
let a = 1; // SyntaxError: Identifier 'a' has already been declared
}
try {
} catch (e) {
let e; // SyntaxError: Identifier 'e' has already been declared
}
Если вы экспериментируете в REPL, например, в веб-консоли Firefox (Инструменты > Веб-разработчик > Веб-консоль) и выполняете два объявления let с одинаковым именем в двух разных вводах, вы можете получить ту же ошибку переобъявления. См. дальнейшее обсуждение этой проблемы в ошибке Firefox 1580891. Консоль Chrome допускает переобъявления let между различными входами REPL.
Вы можете столкнуться с ошибками в switch выражениях, поскольку существует только один блок.
let x = 1;
switch (x) {
case 0:
let foo;
break;
case 1:
let foo; // SyntaxError: Identifier 'foo' has already been declared
break;
}
Чтобы избежать ошибки, заключите каждое case в новое блочное выражение.
let x = 1;
switch (x) {
case 0: {
let foo;
break;
}
case 1: {
let foo;
break;
}
}
Примеры
Правила области видимости
Переменные, объявленные с помощью let, имеют свою область видимости в блоке, для которого они объявлены, а также в любых вложенных подблоках. Таким образом, let работает очень похоже на var. Основное отличие заключается в том, что область видимости переменной var — это вся заключающая функция:
function varTest() {
var x = 1;
{
var x = 2; // same variable!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
{
let x = 2; // different variable
console.log(x); // 2
}
console.log(x); // 1
}
На верхнем уровне программ и функций let, в отличие от var, не создает свойство в глобальном объекте. Например:
var x = "global"; let y = "global"; console.log(this.x); // "global" console.log(this.y); // undefined
TDZ в сочетании с лексической областью видимости
Следующий код приводит к ReferenceError в показанной строке:
function test() {
var foo = 33;
if (foo) {
let foo = foo + 55; // ReferenceError
}
}
test();
Блок if вычисляется, потому что внешняя var foo имеет значение. Однако из-за лексической области видимости это значение недоступно внутри блока: идентификатор foo внутри блока if — это let foo. Выражение foo + 55 вызывает ReferenceError, потому что инициализация let foo еще не завершена — она все еще находится во временной мертвой зоне.
Это явление может сбивать с толку в ситуации, подобной следующей. Инструкция let n of n.a уже находится в области видимости блока цикла for...of. Таким образом, идентификатор n.a разрешается в свойство a объекта n, расположенного в первой части самой инструкции (let n). Это все еще находится во временной мертвой зоне, так как его инструкция объявления еще не достигнута и не завершена.
function go(n) {
// n here is defined!
console.log(n); // { a: [1, 2, 3] }
for (let n of n.a) {
// ^ ReferenceError
console.log(n);
}
}
go({ a: [1, 2, 3] });
Другие ситуации
При использовании внутри блока let ограничивает область видимости переменной этим блоком. Обратите внимание на разницу с var, область видимости которой находится внутри функции, где она объявлена.
var a = 1;
var b = 2;
{
var a = 11; // the scope is global
let b = 22; // the scope is inside the block
console.log(a); // 11
console.log(b); // 22
}
console.log(a); // 11
console.log(b); // 2
Однако эта комбинация объявлений var и let ниже является SyntaxError, потому что var не имеет блочной области видимости, что приводит к их нахождению в одной области видимости. Это приводит к неявному переобъявлению переменной.
let x = 1;
{
var x = 2; // SyntaxError for re-declaration
}
Объявление с деструктуризацией
Левая часть каждого = также может быть шаблоном привязки. Это позволяет создавать несколько переменных одновременно.
const result = /(a+)(b+)(c+)/.exec("aaabcc");
let [, a, b, c] = result;
console.log(a, b, c); // "aaa" "b" "cc"
Дополнительную информацию см. в разделе Деструктуризация.
Спецификации
Совместимость с браузерами
| Настольные | Мобильные | Серверные | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Opera | Safari | Chrome Android | Firefox for Android | Opera Android | Safari on iOS | Samsung Internet | WebView Android | WebView on iOS | Bun | Deno | Node.js | |
let |
49
41–49Требуется строгий режим. |
14
12–14В Edge 12 и 13let внутри инициализатора цикла for не создает отдельную переменную для каждой итерации цикла, как определено ES2015. Вместо этого он ведет себя так, как если бы цикл был обернут в блок области видимости с let непосредственно перед циклом. |
44До Firefox 44let доступен только для блочных выражений в HTML, обернутых в блок <script type="application/javascript;version=1.7"> (или выше), и имеет другую семантику (например, отсутствует временная мертвая зона).До Firefox 46 при переобъявлении вместо SyntaxError выбрасывается TypeError.Firefox 54 добавляет поддержку let в воркерах. |
17 |
10 |
49
41–49Требуется строгий режим. |
44До Firefox для Android 44let доступен только для блочных выражений в HTML, обернутых в блок <script type="application/javascript;version=1.7"> (или выше), и имеет другую семантику (например, отсутствует временная мертвая зона).До Firefox для Android 46 при переобъявлении вместо SyntaxError выбрасывается TypeError.Firefox для Android 54 добавляет поддержку let в воркерах. |
18 |
10 |
5.0
4.0–5.0Требуется строгий режим. |
49
41–49Требуется строгий режим. |
10 |
1.0.0 |
1.0 |
6.0.0 |
См. также
varconst- Поднятие
-
ES6 In Depth:
letиconstна hacks.mozilla.org (2015) -
Breaking changes in
letandconstin Firefox 44 на blog.mozilla.org (2015) - You Don't Know JS: Scope & Closures, Ch.3: Function vs. Block Scope by Kyle Simpson
- Что такое временная мертвая зона? на Stack Overflow
-
В чем разница между использованием
letиvar? на Stack Overflow - Почему имя 'let' было выбрано для объявлений переменных с блочной областью видимости в JavaScript? на Stack Overflow
© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let