Skip to content

Intermediate · Scope, this, prototypes and classes · topic 21 of 61 · 25 min

Hoisting and the temporal dead zone

Explanation

Before running code, the engine first scans it and creates all the variables and functions of each scope. Function declarations are ready to use from the top; var variables exist from the top but hold undefined; let, const and class exist but cannot be touched until their line runs (the temporal dead zone). This behaviour is called hoisting.

Two phases: creation and execution

When a script or a function starts, the engine does not just read line by line. First, in the creation phase, it finds every declaration in that scope and sets up the names. Then, in the execution phase, it runs the code from the top. The effect is as if declarations were "hoisted" (lifted) to the top of their scope. Nothing actually moves in your file; it is a way of describing this two-step process.

Function declarations are fully hoisted

javascript
console.log(add(2, 3)); // 5: works before the definition

function add(a, b) {
  return a + b;
}
The whole function is created in the creation phase, so it can be called above its line.

var is hoisted as undefined

javascript
console.log(price); // undefined (no error!)
var price = 499;
console.log(price); // 499
The name price exists from the start with the value undefined; the assignment happens later, at its line.

This silent undefined is why var bugs are hard to find: the program keeps running with a wrong value instead of stopping with a clear error.

let, const and class: the temporal dead zone

let, const and class are also hoisted (the engine knows the names), but they are not initialised. From the start of the block until the declaration line runs, the variable is in the temporal dead zone (TDZ). Touching it there throws a ReferenceError. This is GOOD: a loud error is better than a silent undefined.

javascript
console.log(total); // ReferenceError: Cannot access 'total' before initialization
let total = 100;
javascript
let city = 'Pune';
function show() {
  console.log(city); // ReferenceError, not 'Pune'!
  let city = 'Goa';  // this inner city is hoisted to the top of show()
}
show();
The inner let city shadows the outer one for the WHOLE function, so the first line is in its TDZ.

Function expressions and arrows are not callable early

javascript
greet();             // TypeError: greet is not a function (with var)
var greet = function () { console.log('hi'); };

wave();              // ReferenceError (with const: TDZ)
const wave = () => console.log('bye');
DeclarationHoisted?Value before its line
function f() {}Yes, fullyThe working function
var xYesundefined
let x / const xYes, but in the TDZReferenceError if used
class C {}Yes, but in the TDZReferenceError if used
var f = function () {}Only the name fundefined, so calling it is a TypeError

Execution context and the call stack

Each time a function is called, the engine creates an execution context: a record holding that call's variables, its this value and a link to the outer scope. Contexts are pushed onto the call stack and popped off when the function returns. Hoisting happens at the creation of each context.

Careful: Do not rely on hoisting to call things before they are defined, except function declarations used as helpers at the bottom of a file (a common, readable style). Declare variables at the top of the block where they are used, with const or let.
Tip: Interview answer in one line: "Declarations are processed before code runs. Function declarations are usable early, var is undefined early, and let/const/class throw a ReferenceError early because of the temporal dead zone."

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Hoisting and the temporal dead zone · 1/6

Hoisting

In this lesson you will learn what happens before your code runs, which is called hoisting.

Prefer a video by a teacher? Find free videos on “javascript hoisting temporal dead zone explained” on YouTube ↗

In real life: A crash that var would have hidden

In a payroll script, a developer used the bonus variable before calculating it. With var, salaries were silently computed with bonus = undefined, giving NaN in every payslip that went to the bank file. With let, the script would have stopped at once with a ReferenceError pointing to the exact line.

javascript
function netPay(basic) {
  const pay = basic + bonus; // ReferenceError with let: caught in testing
  let bonus = basic * 0.1;
  return pay;
}
try { netPay(30000); } catch (e) { console.log(e.name); } // ReferenceError

Practice (0/2 done)

Task 1 · Code · Javascript

This code calls getTax before it is defined and crashes, because getTax is a const arrow function. Change ONLY the definition into a function declaration so the call works.

Task 2 · Code · Javascript

Write whatHappens(kind) that returns what you get when you read a variable BEFORE its declaration line: for "var" return "undefined", for "let" and "const" return "ReferenceError", for "function" return "works". Anything else returns "unknown".

Quick quiz

Question 1

1.console.log(a); var a = 3; What is printed?

Question 2

2.console.log(b); let b = 3; What happens?

Question 3

3.sayHi(); function sayHi() { return "hi"; } Does the call work?

Question 4

4.f(); var f = () => 1; What happens?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

console.log(a); var a = 3; What is printed?

Answer: undefined

var is hoisted and initialised with undefined.

console.log(b); let b = 3; What happens?

Answer: ReferenceError

b is in the temporal dead zone until its declaration runs.

sayHi(); function sayHi() { return "hi"; } Does the call work?

Answer: Yes

Function declarations are fully hoisted.

f(); var f = () => 1; What happens?

Answer: TypeError: f is not a function

f exists as undefined, and calling undefined is a TypeError.

Why can you call a function declaration above the line where it is written?Related

Answer: Hoisting: function declarations are ready before the code runs

JavaScript notes declarations before running. Function declarations are fully hoisted, unlike let and const.

From Scope, hoisting and closures

function f() { return 1; console.log("hi"); } What happens when f() runs?Related

Answer: Returns 1 and never prints

return ends the function; the next line is never reached.

From Functions: declarations, expressions and return

if (true) { let a = 1; } console.log(a); What happens?Related

Answer: ReferenceError

let is block-scoped, so a does not exist outside the braces.

From Variables: let, const and var

const a = [1, 2, 3]; a[10] = 4; console.log(a.length) prints…Related

Answer: 11

Setting index 10 makes the length 11 (with empty slots).

From Predict-the-output questions

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 4 more topics in “Scope, this, prototypes and classes” before its exam: How this works, Prototypes and the prototype chain, Classes, Inheritance and composition.
  2. Module 5 exam: Scope, this, prototypes and classes · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Modules, errors, JSON and dates. 61 topics left in the course before the final exam.

← Previous: References, copies and equality