ES6 - Temporal Dead Zone
ES6 - Temporal Dead Zone

Ostatnio poznałem bardzo fajną ciekawostkę. Let i Const, wbrew powszechnej opinii, są hoistowane, ale w specjalny sposób. Czas wyjaśnić, o co w tym chodzi, i czym jest Temporal Dead Zone.

Większość osób twierdzi, że let i const nie są hoistowane. Jest to naturalne, kiedy przeprowadzi się prostsze testy:

console.log(a); // undefined
var a = 2;
console.log(b); // Uncaught ReferenceError: b is not defined
console.log(c); // Uncaught ReferenceError: c is not defined
let c = 2;

Widzimy tutaj, że i var i let nie pozwala wypisać nic przed przypisaniem wartości do zmiennej. Co dziwne, wypisanie zmiennej let przed deklaracją daje taki sam wynik jak wypisanie nieistniejącej zmiennej. Mogłoby z tego wynikać, że hoisting nie działa, bo jest błąd. Pogrzebmy jednak głębiej:

var zmienna = 'wartosc zewnetrzna';
(function() {
  console.log(zmienna); // wartosc zewnetrzna
}());
 
let zmienna2 = 'wartosc zewnetrzna';
(function() {
  console.log(zmienna2); // wartosc zewnetrzna
}());

W tym przykładzie stworzyliśmy zmienną, a później wypisaliśmy ją w scope niżej. Przez to, że w scope funkcji zmiennych nie ma, są szukane wyższym zasięgu, dlatego mają wartośći takie, jak zmienna i zmienna2. A teraz dodajmy małą zmianę:

var zmienna = 'wartosc zewnetrzna';
(function() {
  console.log(zmienna); // undefined
  var zmienna = 'wartosc wewnetrzna';
}());
 
let zmienna2 = 'wartosc zewnetrzna';
(function() {
  console.log(zmienna2); // Uncaught ReferenceError: zmienna2 is not defined
  let zmienna2 = 'wartosc wewnetrzna';
}());

Co otrzymujemy? Takie same wyniki jak w pierwszym przykładzie. Jednak, gdyby hoisting nie działał, to wypisanie zmienna2 powinno zwrócić ‚wartosc zewnetrzna’, prawda? Niestety jednak hoisting działa. Dlaczego więc nie uzyskujemy wartości ‚undefined’ jak w przypadku var? Z powodu Temporal Dead Zone (TDZ).

Podczas czytania funkcji, JavaScript tworzy instancję zmiennej let, tak jak w przypadku var, ale włącza dla nie TDZ. Sprawia ona, że do czasu pierwszego zainicjalizowania zmiennej let każde jej wywołanie zwracać będzie Uncaught ReferenceError:… is not defined. Wygląda to tak:

let zmienna = 'wartosc zewnetrzna';
 (function() {
   // TDZ dla let i const, w tym 'let zmienna' sie zaczyna...
   console.log(zmienna); // Uncaught ReferenceError: zmienna is not defined
   let zmienna = 'wartosc wewnetrzna';
   // TDZ dla 'let zmienna' sie konczy
 }());

Dokumentacja ES6/EcmaScript2015 utwierdza nas w tym przekonaniu:13.2.1 Let and Const Declarations

13.2.1 Let and Const Declarations
NOTE let and const declarations define variables that are scoped to the running execution context’s LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable’s LexicalBinding is evaluated. A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer’s AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created. If a LexicalBindingin a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.

Podsumowanie

Właśnie przez TDZ nie działają nam let i const przed ich zainicjalizowaniem. I dlatego właśnie wiele osób mówi, mylnie, że hoisting na nie nie działa. Hoisting dla let i const działa, tylko inaczej niż dla var.

Dziękuję Comandeer za zwrócenie uwagi na to zjawisko 🙂
Jeżeli masz jakieś pytania, zapraszam do komentarzy.

ZOSTAW ODPOWIEDŹ

Please enter your comment!
Please enter your name here