let

Оголошення let (нехай, покладімо) оголошує повторно присвоювані локальні змінні з блоковою областю видимості, необов'язково ініціалізуючи кожну з них значенням.

Спробуйте його в дії

Синтаксис

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:

Або, якщо нічого з вищепереліченого немає:

  • Поточний модуль – для коду, що працює в модульному режимі
  • Глобальна область видимості – для коду, що працює в сценарному режимі.

Порівняно з 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 починається на початку області видимості
  console.log(bar); // "undefined"
  console.log(foo); // ReferenceError: Cannot access 'foo' before initialization
  var bar = 1;
  let foo = 2; // Кінець TDZ (для foo)
}

Термін "темпоральна" вживається, тому що ця зона залежить від порядку виконання (часу), а не порядку, в якому код написаний (позиції). Наприклад, код нижче працює, тому що, навіть попри те, що функція, що користується змінною let, зустрічається раніше, ніж ця змінна оголошена, функція викликається поза TDZ.

{
  // TDZ починається на початку області видимості
  const func = () => console.log(letVar); // OK

  // В межах TDZ звертання до letVar викидає `ReferenceError`

  let letVar = 3; // Кінець TDZ (для letVar)
  func(); // Викликано поза TDZ!
}

Застосування оператора typeof на змінній у її TDZ викидає ReferenceError:

{
  typeof i; // ReferenceError: Cannot access 'i' before initialization
  let i = 10;
}

Це відрізняється від вживання typeof для невизначених змінних та змінних, що мають значення undefined:

console.log(typeof undeclaredVariable); // "undefined"

[!NOTE] Оголошення 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 'a' 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; // та сама змінна!
    console.log(x); // 2
  }
  console.log(x); // 2
}

function letTest() {
  let x = 1;
  {
    let x = 2; // інша змінна
    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 тут – означено!
  console.log(n); // { a: [1, 2, 3] }

  // ReferenceError на наступному рядку
  for (let n of n.a) {
    console.log(n);
  }
}

go({ a: [1, 2, 3] });

Інші ситуації

Бувши вжитим усередині блоку, let обмежує область видимості змінної цим блоком. Зверніть увагу на різницю з var, чия область видимості – функція, в котрій відбулось оголошення.

var a = 1;
var b = 2;

{
  var a = 11; // область видимості – глобальна
  let b = 22; // область видимості – блок

  console.log(a); // 11
  console.log(b); // 22
}

console.log(a); // 11
console.log(b); // 2

Проте поєднання оголошень var і let нижче – синтаксична помилка, у зв'язку з тим, що var не обмежена блоком, а отже – обидві змінні мають однакову область видимості. Це призводить до неявного повторного оголошення змінної.

let x = 1;

{
  var x = 2; // SyntaxError через повторне оголошення
}

Оголошення з деструктуруванням

Лівий бік кожного = також може бути патерном зв'язування. Це дає змогу створювати кілька змінних за раз.

const result = /(a+)(b+)(c+)/.exec("aaabcc");
let [, a, b, c] = result;
console.log(a, b, c); // "aaa" "b" "cc"

Більше про це – в Присвоєнні з деструктуруванням.

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

Сумісність із браузерами

desktop mobile server
Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet Deno Node.js
let
Chrome Full support 49
Edge Full support 14
Firefox Full support 44
footnote
Internet Explorer Partial support 11
footnote
Opera Full support 17
Safari Full support 10
WebView Android Full support 49
Chrome Android Full support 49
Firefox for Android Full support 44
footnote
Opera Android Full support 18
Safari on iOS Full support 10
Samsung Internet Full support 5.0
Deno Full support 1.0
Node.js Full support 6.0.0

Дивіться також