Я знаю, що Temporal Dead Zone звучить як науково-фантастична фраза. Але корисно зрозуміти, що означають терміни та поняття, з якими ви працюєте щодня (або хочете дізнатись про них).
Прив’яжіть, тому що це ускладнюється.
Чи знаєте ви, що в JavaScript ми можемо додати, { }
щоб додати рівень сфери, де завгодно?
Тому ми завжди могли зробити наступне:
{ { { { { { var madness = true } } } } } }
Я включив цю деталь, щоб переконатися, що майбутні приклади мають сенс (оскільки я не хотів припускати, що всі це знали).
До ES6 не було іншого способу оголосити змінні, крім var
. Але ES6 приніс нам let
і const
.
let
і const
декларації обидва мають блок-область, що означає, що вони доступні лише в {
}
оточуючих їх. var
, з іншого боку, не має цього обмеження.
Ось приклад:
let babyAge = 1; let isBirthday = true; if (isBirthday) { let babyAge = 2; } console.log(babyAge); // Hmmmm. This prints 1
Вищезазначене сталося, оскільки повторне оголошення babyAge
до 2 доступне лише всередині if
блоку. Крім цього, використовується перший babyAge
. Чи бачите ви, що це дві різні змінні?
На відміну від цього, var
декларація не має блокової області:
var babyAge = 1; var isBirthday = true; if (isBirthday) { var babyAge = 2; } console.log(babyAge); // Ah! This prints 2
Остаточна помітна різниця між let
/ const
та var
полягає в тому, що якщо ви отримуєте доступ var
до того, як він був оголошений, він не визначений. Але якщо ви зробите те саме для let
і const
, вони кинуть ReferenceError
.
console.log(varNumber); // undefined console.log(letNumber); // Doesn't log, as it throws a ReferenceError letNumber is not defined var varNumber = 1; let letNumber = 1;
Вони викидають помилку все через тимчасову мертву зону.
Пояснення тимчасової мертвої зони
Ось що таке TDZ: термін, що описує стан, коли змінні недоступні. Вони входять у сферу дії, але не оголошуються.
let
іconst
змінні існують у TDZ від початку їхньої обширної області дії до моменту їх оголошення.
Ви також можете сказати, що змінні існують у TDZ з місця, де вони прив'язуються (коли змінна прив'язується до області дії, яка знаходиться всередині), доки вона не буде оголошена (коли ім'я зарезервовано в пам'яті для цієї змінної).
{ // This is the temporal dead zone for the age variable! // This is the temporal dead zone for the age variable! // This is the temporal dead zone for the age variable! // This is the temporal dead zone for the age variable! let age = 25; // Whew, we got there! No more TDZ console.log(age); }
Ви можете бачити вище, що якби я звернувся до змінної віку раніше, ніж її оголошення, це викинуло б ReferenceError
. Через TDZ.
Але var
не буде цього робити. var
просто ініціалізується за замовчуванням, на undefined
відміну від іншого оголошення.
Яка різниця між декларуванням та ініціалізацією?
Ось приклад оголошення змінної та ініціалізації змінної.
function scopeExample() { let age; // 1 age = 20; // 2 let hands = 2; // 3 }
Оголошення змінної означає, що ми зберігаємо ім'я в пам'яті на поточному рівні. Це позначено в коментарях 1.
Ініціалізація змінної - це встановлення значення змінної. Це позначено в коментарях 2.
Або ви завжди можете робити обидва на одному рядку. Це позначено в коментарях 3.
Просто, щоб повторити ще раз: let
іconst
змінні існують у TDZ від початку їхньої обширної області дії до моменту їх оголошення.
Тож із наведеного вище фрагмента коду, де TDZ age
? Крім того, чи hands
є TDZ? Якщо так, де початок і кінець TDZ для рук?
TDZ для рук закінчується, коли його оголошують, той самий рядок, який він встановлює на 2.
TZ для віку закінчується, коли його оголошують, а ім'я зарезервовано в пам'яті (на кроці 2, де я коментував).
Чому TDZ створюється, коли він є?
Повернемось до нашого першого прикладу:
{ // This is the temporal dead zone for the age variable! // This is the temporal dead zone for the age variable! // This is the temporal dead zone for the age variable! // This is the temporal dead zone for the age variable! let age = 25; // Whew, we got there! No more TDZ console.log(age); }
Якщо ми додамо console.log
всередині TDZ, ви побачите цю помилку:

Чому TDZ існує між вершиною області дії та оголошенням змінної? Яка конкретна причина цього?
Це через підняття.
Механізм JS, який аналізує та виконує ваш код, має виконати 2 кроки:
- Синтаксичний аналіз коду в абстрактному дереві синтаксису / виконуваному байтовому коді та
- Виконання часу виконання.
Крок 1 - це місце, де відбувається підняття, і це робиться двигуном JS. Це по суті перемістить усі ваші декларації змінних до початку їхньої області дії. Отже, прикладом може бути:
console.log(hoistedVariable); // undefined var hoistedVariable = 1;
Щоб бути зрозумілим, ці змінні фізично не рухаються в коді. Але результат буде функціонально ідентичним наведеному нижче:
var hoistedVariable; console.log(hoistedVariable); // undefined counter = 1;
Єдина відмінність між const
і let
полягає в тому, що коли їх піднімають, їх значення не отримують за замовчуванням undefined
.
Щоб довести, let
а const
також підняти, ось приклад:
{ // Both the below variables will be hoisted to the top of their scope! console.log(typeof nonsenseThatDoesntExist); // Prints undefined console.log(typeof name); // Throws an error, cannot access 'name' before initialization let name = "Kealan"; }
Наведений фрагмент є доказом того, що let
він чітко піднятий вище там, де він був оголошений, оскільки двигун попереджає нас про факт. Він знає, що name
існує (він оголошений), але ми не можемо отримати до нього доступ до його ініціалізації.
Якщо це допомагає вам пам’ятати, думайте про це так.
When variables get hoisted, var
gets undefined
initialized to its value by default in the process of hoisting. let
and const
also get hoisted, but don't get set to undefined
when they get hoisted.
And that's the sole reason we have the TDZ. Which is why it happens with let
and const
but not var
.
More examples of the TDZ
The TDZ can also be created for default function parameters. So something like this:
function createTDZ(a=b, b) { } createTDZ(undefined, 1);
throws a ReferenceError
, because the evaluation of variable a
tries to access variable b
before it has been parsed by the JS engine. The function arguments are all inside the TDZ until they are parsed.
Even something as simple as let tdzTest = tdzTest;
would throw an error due to the TDZ. But var
here would just create tdzTest
and set it to undefined
.
There's one more final and fairly advanced example from Erik Arvindson (who's involved in evolving and maintaining the ECMAScript spec):
let a = f(); // 1 const b = 2; function f() { return b; } // 2, b is in the TDZ
You can follow the commented numbers.
In the first line we call the f
function, and then try to access the b
variable (which throws a ReferenceError
because b
is in the TDZ).
Why do we have the TDZ?
Dr Alex Rauschmayer has an excellent post on why the TDZ exists, and the main reason is this:
It helps us catch errors.
To try and access a variable before it is declared is the wrong way round, and shouldn't be possible.
It also gives more expected and rational semantics for const
(because const
is hoisted, what happens if a programmer tries to use it before it is declared at runtime? What variable should it hold at the point when it gets hoisted?), and was the best approach decided by the ECMAScript spec team.
How to avoid the issues the TDZ causes
Relatively simply, always make sure you define your let
s and const
s at the top of your scope.