What is TDZ? from var to let and const ( JavaScript ES6)
Tags: Web Development, ES6, Javascript Development, Javascript Tips, JavaScript

let and const were introduced in ES6, and along with them the temporal dead zone (TDZ). It isn’t the most recent addition to JavaScript, but I think that many developers still aren’t familiar with it. In this article I’ll explain what TDZ is and why it exists, and I’ll discuss some of the differences between var and let/const, and touch on topics like hoisting along the way.

This article is suitable for both beginners and experienced JavaScript developers wanting to brush up on the topic.

Let me start off with the following definition of TDZ from MDN. I’m going to break it apart, so don’t worry if it’s not clear at first. Also note that it isn’t completely accurate, but we’ll get to that.

In ECMAScript 2015, let bindings are not subject to Variable Hoisting, which means that let declarations do not move to the top of the current execution context. Referencing the variable in the block before the initialization results in a ReferenceError (contrary to a variable declared with var, which will just have the undefined value). The variable is in a “temporal dead zone” from the start of the block until the initialization is processed

One of the differences between let and var is that var is hoisted to the top of execution context. So, for example, if we’d run the following function:

The output is undefined. the variable x was hoisted to the top of the function, as if it was declared “var x;”. We log it to the console before giving it any value so the output is undefined.

Hoisted variables aren’t really physically moved to the top of the function and declared. It is meant in a more figurative way. The variable is put into memory and initialized with a value of undefined when the code is compiled. This takes place before the code is actually executed (the creation and execution phases). The result however is similar to moving variable declarations to the top of the scope without assigning them a value.

If we try the previous example using let, the result will be different:

We received “ReferenceError: x is not defined”. As MDN says, let isn’t hoisted like var is. In other words, x isn’t initialized when we’re trying to log it to the console. Trying to access an uninitialized variable causes a ReferenceError.

Although the explanation from MDN says that “let bindings aren’t subject to Variable Hoisting”, you could find contradictory information claiming that they are in fact hoisted.

When the scope of a let variable is entered, storage space (a binding) is created for it. The engine is aware of its existence, so you could say that it is in that sense hoisted, although it is not initialized. Look at the example to see how let is hoisted (source):

This shows a ReferenceError, but if we remove line 4 we’ll see ‘outer scope’. The existence of line 4 changed the output of line 3. The period between entering a scope and the execution reaching the actual declaration (line 4) of a let variable is precisely the temporal dead zone. This example demonstrates the TDZ and that a let variable is actually hoisted in a sense.

If we give the explanation from MDN the benefit of the doubt, it is simply referring to the specific way var is hoisted, or in other words, to the life cycle of a var declared variable. let isn’t hoisted in that sense, but it is in the sense that the engine is aware of its existence.

As a side note, it’s noteworthy that TDZ is temporal. After all it is called the temporal dead zone. Meaning that it relates to time, as in the time of execution, and not the physical location in the code. See the following example:

Line 3 doesn’t throw an error because by the time it is executed our variable has already been initialized.

All in all it looks like TDZ is good for us developers. Accessing a variable before it’s declared isn’t something you’ll usually be doing on purpose, so it’s a pretty useful addition that could potentially prevent some issues.

However, you may be surprised to know that the main reason for implementing TDZ wasn’t helping you avoid errors. After all, trying to access variables before they are declared isn’t one of the most common errors around. The implementation of TDZ has to do with the addition of const.

const is another keyword added is ES6. Basically it is used to declare constants. Let me quote MDN again here:

Constants are block-scoped, much like variables defined using the let keyword. The value of a constant can't be changed through reassignment, and it can't be redeclared.

According to Allen Wirfs-Brock, the project editor of ES6, the main motivation for TDZ was to make const work. it was then applied to let mostly for the sake of consistency. let could probably work fine without TDZ, but const needed a “rational semantic” to make it work (source ).

If const behaved like var then it may have been possible, for example, to assign a value to a hoisted const before reaching the actual declaration. It works with var:

In other words. TDZ’s goal is to make const assignment only possible at the declaration time. It should be defined once (declared and initialized with a value), and accessing it should return that value.

Note however that although const cant be given a new value through reassignment, and it cannot be redeclared, its value can be modified in some cases. For example, if myObj is a constant we can give it new properties and change their values as usual. we cannot however reassign myObj itself.

A case can be made that if consistency was important then let should have behaved like var. It seems that being able to refractor between let and const without unexpected surprises was one of the reasons that TDZ was applied to both of them.

On the other hand, some might say that there is no longer a use for var. If that is the case then why would we care about it? Wirfs-Brock noted that the goal of let is to provide a variable that is block scoped. Not to replace var per se. With that in mind, there may be cases where var could be more suitable than let and still be used. This is beyond the scope of this article however, so if you’re interested check out The case for var by Kyle Simpson.

In conclusion, we talked about hosting, how let/const are technically hoisted but in a different way than var, what is TDZ and why it was added, and we touched on other issues like should there even be a TDZ and is var still relevant.

If you have any questions regarding this post or any suggestions, corrections or notes, feel free to comment, email or DM me. Thanks for reading!

Sources and further reading:

TDZ in MDN web docs

Hoisting in MDN web docs

Chapter on TDZ in “Exploring ES6” by Dr. Axel Rauschmayer.

Performance concern with let/const for some arguments against TDZ

Photos by Caspar Camille Rubin on Unsplash

Thanks to Alon Valadji.