Was ist die Temporal Dead Zone (TDZ) in JavaScript?

Ich weiß, dass Temporal Dead Zone wie eine Science-Fiction-Phrase klingt. Es ist jedoch hilfreich zu verstehen, was die Begriffe und Konzepte bedeuten, mit denen Sie täglich arbeiten (oder die Sie kennenlernen möchten).

Schnall dich an, denn das wird kompliziert.

Ist Ihnen bewusst, dass wir in JavaScript einen Bereich hinzufügen können, { }wo immer wir wollen?

Wir könnten also immer Folgendes tun:

{ { { { { { var madness = true } } } } } }

Ich habe dieses Detail aufgenommen, um sicherzustellen, dass die kommenden Beispiele sinnvoll sind (da ich nicht davon ausgehen wollte, dass es jeder wusste).

Vor ES6 gab es keine andere Möglichkeit, Variablen zu deklarieren als var. Aber ES6 brachte uns letund const.

letund constDeklarationen haben beide einen Blockumfang, was bedeutet, dass sie nur in der {}Umgebung zugänglich sind. varAuf der anderen Seite gibt es diese Einschränkung nicht.

Hier ist ein Beispiel:

let babyAge = 1; let isBirthday = true; if (isBirthday) { let babyAge = 2; } console.log(babyAge); // Hmmmm. This prints 1

Dies ist aufgetreten, weil die erneute Deklaration von babyAgebis 2 nur innerhalb des ifBlocks verfügbar ist . Darüber hinaus wird der erste babyAgeverwendet. Können Sie sehen, dass es sich um zwei verschiedene Variablen handelt?

Im Gegensatz dazu hat die varDeklaration keinen Blockumfang:

var babyAge = 1; var isBirthday = true; if (isBirthday) { var babyAge = 2; } console.log(babyAge); // Ah! This prints 2

Der letzte wesentliche Unterschied zwischen let/ constund varbesteht darin, dass der Zugriff varundefiniert ist, wenn Sie vor der Deklaration darauf zugreifen . Aber wenn Sie das gleiche für letund tun const, werfen sie eine 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;

Sie werfen den Fehler alle wegen der zeitlichen Totzone.

Temporal Dead Zone erklärt

Dies ist, was die TDZ ist: der Begriff, der den Zustand beschreibt, in dem Variablen nicht erreichbar sind. Sie sind im Geltungsbereich, aber nicht deklariert.

Das letundconstVariablen existieren in der TDZ vom Beginn ihres umschließenden Bereichs bis zu ihrer Deklaration.

Sie können auch sagen, dass die Variablen in der TDZ von der Stelle, an der sie gebunden werden (wenn die Variable an den Bereich gebunden wird, in dem sie sich befindet) bis zur Deklaration existieren (wenn ein Name für diese Variable im Speicher reserviert ist).

{ // 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); }

Sie können oben sehen, dass, wenn ich früher als in der Deklaration auf die Altersvariable zugegriffen habe, a ausgelöst wird ReferenceError. Wegen der TDZ.

Aber das vargeht nicht. varwird nur standardmäßig im undefinedGegensatz zur anderen Deklaration initialisiert .

Was ist der Unterschied zwischen Deklarieren und Initialisieren?

Hier ist ein Beispiel für das Deklarieren einer Variablen und das Initialisieren einer Variablen.

function scopeExample() { let age; // 1 age = 20; // 2 let hands = 2; // 3 }

Wenn Sie eine Variable deklarieren, behalten wir den Namen im aktuellen Bereich im Speicher. Das ist in den Kommentaren mit 1 gekennzeichnet.

Beim Initialisieren einer Variablen wird der Wert der Variablen festgelegt. Das ist in den Kommentaren mit 2 gekennzeichnet.

Oder Sie können immer beides in einer Zeile tun. Das ist in den Kommentaren mit 3 gekennzeichnet.

Nur um mich noch einmal zu wiederholen: das letundconstVariablen existieren in der TDZ vom Beginn ihres umschließenden Bereichs bis zu ihrer Deklaration.

Woher stammt die TDZ aus dem obigen Code-Snippet age? Auch dann , wenn handsein TDZ haben? Wenn ja, wo ist der Anfang und das Ende der TDZ für Hände?

Überprüfen Sie Ihre Antwort Die Hände und Altersvariablen werden beide in die TDZ eingegeben.

Die TDZ für Hände endet, wenn sie deklariert wird. Dieselbe Zeile wird auf 2 gesetzt.

Die TZ für das Alter endet, wenn sie deklariert und der Name im Speicher reserviert wird (in Schritt 2, wo ich einen Kommentar abgegeben habe).

Warum wird das TDZ erstellt, wenn es ist?

Kehren wir zu unserem ersten Beispiel zurück:

{ // 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); }

Wenn wir ein console.loginnerhalb der TDZ hinzufügen, wird dieser Fehler angezeigt:

Warum existiert die TDZ zwischen dem oberen Rand des Bereichs und der Variablendeklaration? Was ist der spezifische Grund dafür?

Es ist wegen des Hebens.

Die JS-Engine, die Ihren Code analysiert und ausführt, muss zwei Schritte ausführen:

  1. Parsen des Codes in einen abstrakten Syntaxbaum / ausführbaren Bytecode und
  2. Laufzeitausführung.

In Schritt 1 erfolgt das Heben, und dies wird von der JS-Engine durchgeführt. Im Wesentlichen werden alle Ihre Variablendeklarationen an die Spitze ihres Gültigkeitsbereichs verschoben. Ein Beispiel wäre also:

console.log(hoistedVariable); // undefined var hoistedVariable = 1;

To be clear, these variables aren't physically moving in the code. But, the result would be functionally identical to the below:

var hoistedVariable; console.log(hoistedVariable); // undefined counter = 1;

The only difference between const and let is that when they are hoisted, their values don't get defaulted to undefined.

Just to prove let and const also hoist, here's an example:

{ // 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"; }

The above snippet is proof that let is clearly hoisted above where it was declared, as the engine alerts us to the fact. It knows name exists (it's declared), but we can't access it before it is initialized.

If it helps you to remember, think of it like this.

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 lets and consts at the top of your scope.