Spec-Zone.ru › JavaScript

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"

Дополнительную информацию см. в разделе Деструктуризация.

Спецификации

Спецификация
ECMAScript® 2027 Language Specification
# sec-let-and-const-declarations

Совместимость с браузерами

Настольные Мобильные Серверные
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 и 13 let внутри инициализатора цикла for не создает отдельную переменную для каждой итерации цикла, как определено ES2015. Вместо этого он ведет себя так, как если бы цикл был обернут в блок области видимости с let непосредственно перед циклом.
44До Firefox 44 let доступен только для блочных выражений в HTML, обернутых в блок <script type="application/javascript;version=1.7"> (или выше), и имеет другую семантику (например, отсутствует временная мертвая зона).
До Firefox 46 при переобъявлении вместо SyntaxError выбрасывается TypeError.
Firefox 54 добавляет поддержку let в воркерах.
17
10
49
41–49Требуется строгий режим.
44До Firefox для Android 44 let доступен только для блочных выражений в 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

См. также

  • var
  • const
  • Поднятие
  • ES6 In Depth: let и const на hacks.mozilla.org (2015)
  • Breaking changes in let and const in 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API