let & const in JS: Hoisting and the Temporal Dead Zone (TDZ)
In modern JavaScript (ES6+), variable declaration evolved significantly with the introduction of let and const. A common misconception is that let and const declarations are not hoisted. In reality, they are hoisted, but they follow an entirely different lifecycle compared to var. Understanding this lifecycle requires examining memory allocation phases, the Temporal Dead Zone (TDZ), and the exact JavaScript runtime errors (ReferenceError, SyntaxError, and TypeError) that govern variable access and assignment.
1. Are let and const Hoisted?
The short answer is yes, let and const declarations are hoisted in JavaScript. However, they are hoisted differently than var.
When developers claim that let and const are not hoisted, it is usually because attempting to access them before their declaration line throws an error, unlike var, which evaluates to undefined.
console.log(b); // Output: undefined
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 10;
var b = 100;
If a were not hoisted at all, the engine would not even know that a exists in the script. Instead, the JavaScript engine alerts us with a very specific message:
ReferenceError: Cannot access 'a' before initialization
This proves the engine is already aware of the identifier a before the line is reached. The variable has been allocated memory, but it resides in a specialized state called the Temporal Dead Zone (TDZ).
2. Memory Allocation: Global Scope vs. Script Space
During the Memory Creation Phase (Phase 1 of execution context creation), JavaScript allocates memory for all declarations before executing a single line of code.
varDeclarations: Memory is allocated and attached directly to the global object (windowin browser environments). It is immediately initialized with the placeholder valueundefined.letandconstDeclarations: Memory is also allocated during the creation phase, but it is stored in a separate, dedicated memory space (often labeled Script or Block in DevTools) rather than being attached to the globalwindowobject. Crucially, they remain uninitialized until execution reaches their declaration line.
Because a and c are not attached to the global object:
var b = 100;
let a = 10;
console.log(window.b); // 100
console.log(window.a); // undefined (not present on the global object)
console.log(this.b); // 100
console.log(this.a); // undefined
3. The Temporal Dead Zone (TDZ) Defined
The Temporal Dead Zone (TDZ) is the time span between when a variable is hoisted (memory is allocated) and when it is formally initialized with a value during the code execution phase.
Lifecycle Breakdown:
- Hoisting Phase: Memory is reserved for
let a. Variable enters the TDZ. - TDZ Window: Any read or write access to
awithin this window immediately throws:ReferenceError: Cannot access 'a' before initialization. - Declaration / Assignment Line: The JavaScript engine executes
let a = 10;. The identifier is initialized. The TDZ ends. - Post-TDZ Execution:
ais freely accessible anywhere downstream in scope.
4. Comparing Runtime Errors: ReferenceError Variations
It is critical to distinguish between two distinct forms of ReferenceError encountered in JavaScript:
// Case 1: Variable declared with let/const accessed prior to initialization
console.log(a);
// Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 10;
// Case 2: Identifier never declared anywhere in the program
console.log(x);
// Uncaught ReferenceError: x is not defined
5. Duplicate Identifiers and SyntaxError
var is very lenient and permits developers to redeclare variables with identical names within the same execution scope. let and const enforce strict scoping discipline and reject duplicate declarations.
var b = 10;
var b = 20; // Valid: b is simply overwritten
let a = 10;
let a = 100;
// Uncaught SyntaxError: Identifier 'a' has already been declared
Even mixing var and let with the same identifier name inside the same scope results in an immediate syntax error:
let a = 10;
var a = 100; // Uncaught SyntaxError: Identifier 'a' has already been declared
Note: A
SyntaxErrorprevents code execution before it even begins. The JavaScript engine performs lexical analysis and parsing prior to code execution. If a duplicate identifier is found, the engine rejects the entire script immediately—no preceding lines are executed.
6. Rules and Constraints of const
The const keyword is even stricter than let. It shares hoisting and TDZ behavior with let, but introduces two immutable constraints:
1. Compulsory Initialization on the Same Line
Unlike var or let, a const variable cannot be declared without an immediate assignment:
// Valid
let a;
a = 10;
// Invalid
const c;
// Uncaught SyntaxError: Missing initializer in const declaration
2. Immutability of Identifier Binding (TypeError)
Once a const identifier is initialized, its variable binding cannot be reassigned:
const c = 1000;
c = 2000;
// Uncaught TypeError: Assignment to constant variable.
7. The Three Major JavaScript Errors Compared
Developers frequently encounter three core errors when working with variable declarations. Knowing which error occurs during which engine phase clarifies how the engine executes code:
8. Best Practices for Variable Declarations
To avoid runtime pitfalls, unpredictable scoping behavior, and TDZ bugs, follow this industry-standard hierarchy:
- Use
constby default: Prioritizeconstfor almost all declarations. It ensures that variable bindings remain stable and cannot be inadvertently overwritten. - Use
letwhen reassignment is explicitly needed: Reserveletfor loop counters, accumulators, state flags, and values that must mutate. - Discontinue
var:varexposes code to unintended global object pollution, silent re-declaration bugs, and confusingundefinedhoisting states. - Shrink the TDZ to zero: Always declare and initialize variables at the absolute top of their corresponding scope. Doing so resolves the variable before any business logic executes, effectively reducing the window of the Temporal Dead Zone to zero.
Summary and Key Takeaways
letandconstare hoisted: They allocate memory during the creation phase just likevar, but they reside in a separate declarative memory space rather than on the globalwindowobject.- The Temporal Dead Zone (TDZ): The time interval between a
letorconstvariable's hoisting and its formal declaration/initialization line. Accessing the variable in this zone throws aReferenceError. ReferenceErrordistinctions:Cannot access 'a' before initializationindicates an existing variable held within the TDZ, whereasx is not definedindicates an identifier missing entirely from scope.- No re-declarations:
letandconstthrow aSyntaxErrorif redeclared within the same scope, aborting script execution during parsing. conststrictness:constdeclarations demand an inline initializer at declaration time (or throwSyntaxError) and prohibit variable reassignment (or throwTypeError).- Global object isolation: Unlike
var, variables declared withletandconstare not attached as properties towindowor the global scope object. - Best practice workflow: Use
constfirst,letwhen reassignments are necessary, avoidvar, and declare variables at the very top of each scope to eliminate TDZ issues.