Qu'est-ce que la zone temporelle morte (TDZ) en JavaScript?

Je sais que Temporal Dead Zone ressemble à une phrase de science-fiction. Mais il est utile de comprendre ce que signifient les termes et les concepts avec lesquels vous travaillez quotidiennement (ou sur lesquels vous souhaitez en savoir plus).

Attachez-vous, car cela se complique.

Savez-vous qu'en JavaScript, nous pouvons ajouter { }pour ajouter un niveau de portée où nous voulons?

Nous pourrions donc toujours faire ce qui suit:

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

J'ai inclus ce détail pour m'assurer que les exemples à venir ont du sens (car je ne voulais pas supposer que tout le monde le savait).

Avant ES6, il n'y avait pas d'autre moyen de déclarer des variables que var. Mais ES6 nous a apporté letet const.

letet les constdéclarations sont toutes deux à portée de bloc, ce qui signifie qu'elles ne sont accessibles que dans {}leur environnement. var, d'autre part, n'a pas cette restriction.

Voici un exemple:

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

Ce qui précède s'est produit car la re-déclaration de babyAgeto 2 n'est disponible qu'à l'intérieur du ifbloc. Au-delà, le premier babyAgeest utilisé. Pouvez-vous voir qu'il s'agit de deux variables différentes?

En revanche, la vardéclaration n'a pas de portée de bloc:

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

La dernière différence majeure entre let/ constet varest que si vous y accédez varavant qu'il ne soit déclaré, il n'est pas défini. Mais si vous faites la même chose pour letet const, ils lancent un 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;

Ils lancent l'erreur à cause de la zone morte temporelle.

Explication de la zone morte temporelle

C'est ce qu'est le TDZ: le terme pour décrire l'état où les variables sont inaccessibles. Ils sont dans la portée, mais ils ne sont pas déclarés.

Le letetconstles variables existent dans la TDZ depuis le début de leur portée englobante jusqu'à ce qu'elles soient déclarées.

Vous pouvez également dire que les variables existent dans la TDZ à partir de l'endroit où elles sont liées (lorsque la variable est liée à la portée dans laquelle elle se trouve) jusqu'à ce qu'elle soit déclarée (lorsqu'un nom est réservé en mémoire pour cette 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! // This is the temporal dead zone for the age variable! let age = 25; // Whew, we got there! No more TDZ console.log(age); }

Vous pouvez voir ci-dessus que si j'accédais à la variable age avant sa déclaration, cela lèverait un ReferenceError. À cause du TDZ.

Mais varne fera pas ça. varest simplement initialisé par défaut à la undefineddifférence de l'autre déclaration.

Quelle est la différence entre déclarer et initialiser?

Voici un exemple de déclaration d'une variable et d'initialisation d'une variable.

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

Déclarer une variable signifie que nous réservons le nom en mémoire à la portée actuelle. C'est étiqueté 1 dans les commentaires.

L'initialisation d'une variable définit la valeur de la variable. C'est étiqueté 2 dans les commentaires.

Ou vous pouvez toujours faire les deux sur une seule ligne. C'est étiqueté 3 dans les commentaires.

Juste pour me répéter: le letetconstles variables existent dans la TDZ depuis le début de leur portée englobante jusqu'à ce qu'elles soient déclarées.

Donc, à partir de l'extrait de code ci-dessus, à quoi sert le TDZ age? Aussi, a-t hands-il un TDZ? Si oui, où se trouvent le début et la fin de la TDZ pour les mains?

Vérifiez votre réponse Les variables mains et âge entrent toutes deux dans la TDZ.

Le TDZ pour les mains se termine quand il est déclaré, la même ligne qu'il est mis à 2.

Le TZ pour l'âge se termine lorsqu'il est déclaré, et le nom réservé en mémoire (à l'étape 2, où j'ai commenté).

Pourquoi le TDZ est-il créé alors?

Revenons à notre premier exemple:

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

Si nous ajoutons un console.logà l'intérieur du TDZ, vous verrez cette erreur:

Pourquoi le TDZ existe-t-il entre le haut de la portée et la déclaration de variable? Quelle est la raison spécifique à cela?

C'est à cause du levage.

Le moteur JS qui analyse et exécute votre code a 2 étapes à faire:

  1. Analyse du code dans une arborescence de syntaxe abstraite / un code d'octet exécutable, et
  2. Exécution au moment de l'exécution.

L'étape 1 est l'endroit où le levage se produit, et cela est fait par le moteur JS. Cela déplacera essentiellement toutes vos déclarations de variables vers le haut de leur portée. Un exemple serait donc:

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

Pour être clair, ces variables ne se déplacent pas physiquement dans le code. Mais, le résultat serait fonctionnellement identique à celui ci-dessous:

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

La seule différence entre constet letest que lorsqu'ils sont levés, leurs valeurs ne sont pas définies par défaut undefined.

Juste pour prouver letet constaussi hisser, voici un exemple:

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

L'extrait ci-dessus est la preuve qui letest clairement hissée au-dessus de l'endroit où il a été déclaré, car le moteur nous en avertit. Il sait qu'il nameexiste (il est déclaré), mais nous ne pouvons pas y accéder avant qu'il ne soit initialisé.

Si cela vous aide à vous souvenir, pensez-y comme ça.

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.