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
:
- Інструкція блоку
- Інструкція
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 починається на початку області видимості
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 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
let
|
Chrome Full support 49 | Edge Full support 14 | Firefox Full support 44 | Internet Explorer Partial support 11 | Opera Full support 17 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox for Android Full support 44 | 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 |
Дивіться також
var
const
- Підняття
- Заглиблення в ES6:
let
іconst
на hacks.mozilla.org (2015) - Небезпечні зміни
let
іconst
у Firefox 44 на blog.mozilla.org (2015) - Ви не знаєте JS: Області видимості та замикання, р. 3: Функція і блокова область видимості від Кайла Сімпсона
- Що таке Темпоральна мертва зона? на Stack Overflow
- Яка різниця між використанням
let
іvar
? на Stack Overflow - Чому для оголошень змінних з блоковою видимістю в JavaScript було обрано ім'я 'let'? на Stack Overflow