JavaScript Hoisting + Temporal Dead Zone (var vs let/const)

Interview-ready explanation of hoisting and the Temporal Dead Zone: what actually gets hoisted, why let/const behave differently, and common trick questions with outputs.

F

Frontend Interview Team

February 08, 2026

~12 min
JavaScript Hoisting + Temporal Dead Zone (var vs let/const)

Hoisting is one of the most misunderstood JS topics because people explain it as:

“JavaScript moves declarations to the top.”

That’s not wrong, but it’s incomplete.

A better interview-friendly explanation:

During compilation, JS sets up memory for declarations. Then at runtime, code executes line by line.

30‑second interview answer

Hoisting is the JavaScript engine’s behavior of processing declarations before executing code. Function declarations are available before their line. var declarations are hoisted and initialized to undefined, while let/const are hoisted but live in the Temporal Dead Zone (TDZ) until their declaration is evaluated—accessing them early throws a ReferenceError.

Key points

  • Function declarations: usable before they appear.
  • var: hoisted + initialized to undefined.
  • let/const: hoisted + TDZ until initialization.
  • TDZ is about time (before initialization), not block position.

1) What gets hoisted?

✅ Function declarations (fully hoisted)

sayHi();
 
function sayHi() {
  console.log('hi');
}

Works because the function declaration is available before the call.


var declarations (hoisted, initialized to undefined)

console.log(a); // undefined
var a = 10;
console.log(a); // 10

What happens conceptually:

  • declaration var a is hoisted
  • initialization a = 10 stays where it is

⚠️ let / const declarations (hoisted, but in the TDZ)

console.log(x); // ReferenceError
let x = 10;

They are hoisted but not initialized until the declaration is evaluated.

That time between entering the scope and reaching the declaration is the Temporal Dead Zone (TDZ).


2) TDZ in plain English

The TDZ means:

  • the variable exists in the scope
  • but you can’t access it yet
{
  // TDZ starts here for `name`
  // console.log(name); // ReferenceError
  let name = 'Rajesh';
  console.log(name); // Rajesh
  // TDZ ends at the declaration line
}

3) Common interview trick outputs

Example A

var a = 1;
(function () {
  console.log(a);
  var a = 2;
})();

Output: undefined

Why: inside the IIFE, var a is hoisted to the top of that function scope, shadowing outer a.

Equivalent mental rewrite:

(function () {
  var a;
  console.log(a); // undefined
  a = 2;
})();

Example B

let a = 1;
{
  // console.log(a); // ReferenceError
  let a = 2;
}

Why: the inner let a creates a new binding; before it’s initialized, accessing a hits the TDZ.


4) Function expressions vs function declarations

Function expression (behaves like var/let depending on declaration)

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

var say is hoisted to undefined, so calling it gives TypeError.

With let

say(); // ReferenceError (TDZ)
let say = function () {};

5) Interview answers (short + crisp)

Q: What is hoisting?

  • JS allocates memory for declarations before execution.

Q: Why does var show undefined but let throws?

  • var is initialized to undefined during hoisting; let/const are in TDZ until initialized.

Q: Are let/const hoisted?

  • Yes, but they’re not accessible before initialization.

Summary checklist

  • I can explain hoisting as “declarations processed before execution”.
  • I can explain why var is undefined but let throws.
  • I can define TDZ precisely.
  • I know functions vs function expressions differ.

Summary

  • Function declarations: usable before they appear.
  • var: hoisted + initialized to undefined.
  • let/const: hoisted + TDZ until the declaration line.
  • Most interview bugs come from shadowing + assuming var is block-scoped.