JavaScript Hoisting: var, let, and const Explained

Variable hoisting in JavaScript is a mechanism where variable and function declarations are moved to the top of their containing scope during the compilation phase before code execution. While all variable declarations (var, let, and const) are hoisted, they behave differently regarding initialization and accessibility. This guide explains how hoisting works in JavaScript and breaks down the specific behaviors and rules governing var, let, and const.

What is Hoisting?

During the creation phase of the JavaScript execution context, the JavaScript engine scans the code for variable and function declarations and allocates memory for them before executing any code line by line. As a result, variables can be referenced in code before the lines where they are explicitly declared, though the outcome depends on the keyword used to declare them.

Hoisting with var

Variables declared with var are hoisted and automatically initialized with the default value of undefined.

console.log(greeting); // Output: undefined
var greeting = "Hello, World!";
console.log(greeting); // Output: "Hello, World!"

Under the hood, the engine interprets the code as:

var greeting;          // Hoisted and initialized to undefined
console.log(greeting); // undefined
greeting = "Hello, World!";
console.log(greeting); // "Hello, World!"

Hoisting with let and const

Variables declared with let and const are also hoisted to the top of their block scope, but unlike var, they are not initialized.

console.log(count); // Throws ReferenceError: Cannot access 'count' before initialization
let count = 10;

console.log(PI);    // Throws ReferenceError: Cannot access 'PI' before initialization
const PI = 3.14159;

The Temporal Dead Zone (TDZ)

The period between entering the scope where a let or const variable is declared and the actual line where it is initialized is called the Temporal Dead Zone (TDZ). Any attempt to read or write to the variable while it is in the TDZ will result in a runtime ReferenceError.

{
  // TDZ for 'name' starts here
  // console.log(name); // Throws ReferenceError

  let name = "Alice"; // TDZ ends here
  console.log(name);  // Output: "Alice"
}

Key Differences

Feature var let const
Hoisted Yes Yes Yes
Initialized on Hoist Yes (undefined) No No
Subject to TDZ No Yes Yes
Scope Function / Global Block Block
Reassignable Yes Yes No