Master JavaScript

What JavaScript is, and how to run it

11 / 266
Contents

Contents

Master JavaScript

0 of 266 complete

10 min read

Before a name exists

Three different failures that all look like a missing variable, how to tell them apart from the message, and how to reach the global scope on purpose.

There are several ways for a name to be unavailable, and they produce different messages and different amounts of damage. Being able to read which one you are looking at saves a great deal of time, so this lesson meets all of them on purpose.

The first is the one people find most surprising, because the name is right there in the file.

Try CodeJavaScript

It stops with ReferenceError: Cannot access 'x' before initialization. Move the let x = 1; line above the console.log and run it again.

Read the message carefully. It does not say x is not defined. It says x cannot be accessed yet, which means the engine knows about x and is refusing to hand it over.

That is exactly what happens. A name declared with let or const comes into existence at the top of its block, and stays unusable until the line that declares it actually runs. The gap between those two points is called the temporal dead zone, usually shortened to TDZ.

"Temporal" means it is about time rather than place. The dead zone is not a region of the file; it is a stretch of the program's execution. The same line of code can be inside the dead zone on one pass and perfectly fine on the next.

var has no dead zone, and comparing them shows why the dead zone was added.

Try CodeJavaScript

Change both var and the reads to use let instead and compare what you get.

No error at all. The name existed from the start of the program, holding undefined, and only got its 1 when line two ran. This is called hoisting: the declaration behaves as though it were lifted to the top, leaving the assignment where it was written.

That silence is the problem. undefined is a perfectly good value, so a program that reads a variable too early does not stop; it carries on with the wrong number and fails somewhere else entirely. The temporal dead zone turns that into an error at the exact line that caused it.

A name that was never declared at all

Try CodeJavaScript

ReferenceError: neverDeclared is not defined. A different message from the last one, for a different reason. Add let neverDeclared = 1; above it and run it again.

"is not defined" means the engine searched every scope it could see and found no such name anywhere. "Cannot access before initialization" means it found one and it is not ready. Two messages, two causes, and the wording tells you which.

Try CodeJavaScript

Both lines print the same word. Change the second line to let declaredButEmpty = null; and watch only one of them move.

Two identical outputs meaning completely different things. The first is typeof refusing to fail on a name that does not exist, which is its one special power. The second is a name that does exist and holds the value undefined.

What you wroteWhat happensWhat you see
Read a let or const above its own lineStopsReferenceError: Cannot access 'x' before initialization
Read a name declared nowhereStopsReferenceError: x is not defined
typeof a name declared nowhereCarries onThe text "undefined"
Read a declared name never assignedCarries onThe value undefined
Read a var above its own lineCarries onThe value undefined
Five situations that all look like a missing variable.

The global scope, on purpose

Outside every block and every function there is one outermost scope shared by the whole program, called the global scope. Every host keeps it as an object, so its contents can be listed and added to like any other bundle of named values.

Try CodeJavaScript

Change document to JSON and run it again. This is the safe way to ask whether the host has something.

globalThis is the name for that object, and it was only standardised in ES2020. Before that you had to know which host you were in: window in a browser page, self in a worker, global in Node.js. Library code that had to run in all three opened with several lines of guessing.

typeof somethingUndeclared and typeof declaredButEmpty both give "undefined", where declaredButEmpty was declared with let and never assigned. How would you tell the two apart?

You cannot, and the honest answer is worth more than a trick. typeof gives the same word for both, and it is the only way to read a name that might not exist without stopping the program. Asking the global object does not settle it: a top-level let or const never becomes a property of globalThis, so "declaredButEmpty" in globalThis is false, exactly as it is for a name declared nowhere. A top-level var in a classic browser script does join globalThis, so in separates that one case and no other. The reason this is a curiosity rather than a problem is that you should never be in the position of asking: declare every name you use, and the second case is the only one you will meet.

Check yourself

function f() { console.log(String(n)); var n = 3; } and then f();. What is printed?

All lessons in Master JavaScript