let
, const
, and the Temporal Dead Zone (TDZ)
We’ll explore
- Why can we access
var
variables before declaration, but notlet
orconst
? - Are
let
andconst
declarations hoisted? - What exactly is the Temporal Dead Zone (TDZ)?
- What's the difference between SyntaxError, ReferenceError, and TypeError in this context?
Are let
and const
declarations hoisted?
Yes, they absolutely are! 👍🏼
However, they are hoisted differently than var
declarations. While var
variables are hoisted and initialized with undefined
in the global or function scope, let
and const
are hoisted but remain uninitialized. They are placed in a different memory space, separate from the global object.
This leads to a concept called the Temporal Dead Zone (TDZ).
Let's compare:
// Using var
console.log(b); // Output: undefined
var b = 100;
// Using let
console.log(a); // Throws ReferenceError!
let a = 10;
With var b = 100;
, memory is allocated for b
and it's initialized to undefined
before execution. So, console.log(b)
works, even though it appears before the declaration line.
But with let a = 10;
, trying to access a
before the let a = 10;
line results in an error. Why? Because a
is in the Temporal Dead Zone.
What is the Temporal Dead Zone (TDZ)?
The Temporal Dead Zone is the period from the start of the scope until the let
or const
variable is declared and initialized.
During this period:
- The variable has been hoisted (memory space is reserved).
- The variable is not initialized.
- Attempting to access the variable results in a
ReferenceError
.
// Start of TDZ for 'a'
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 10; // End of TDZ for 'a'
console.log(a); // Output: 10
var b = 100;
The TDZ exists to prevent bugs that can occur when accessing variables before they are explicitly assigned a value.
Can we access let
/const
via window?
Since let
and const
variables are not added to the global window
object (unlike var
declared in the global scope), you cannot access them using window.variableName
or this.variableName
(in the global context).
This demonstrates that let
and const
provide better scope encapsulation compared to var
.
Visualizing Hoisting and TDZ
Using browser developer tools helps visualize this:
Notice how a
(declared with let
) is listed under Script
scope and shows <value unavailable>
, indicating it's in the TDZ at that point. In contrast, b
(declared with var
) is directly attached to the Global
object (or Window
) and initialized with undefined
.
❌ Redeclaring Variables
let
You cannot redeclare a let
variable within the same scope.
This also applies if you try to redeclare a let
variable using var
:
A SyntaxError
occurs because the code itself is grammatically incorrect according to JavaScript rules. The engine detects this before execution even begins.
var
You can redeclare a var
variable within the same scope without errors.
index.js | |
---|---|
const
Similar to let
, you cannot redeclare a const
variable within the same scope.
✨ const
Initialization and Re-assignment
Initialization
const
declarations must be initialized with a value on the same line.
index.js | |
---|---|
🔒 Re-assignment
You cannot re-assign a new value to a const
variable after it has been initialized.
A TypeError
occurs when an operation is performed on a value of an inappropriate type, like trying to re-assign a constant. This error happens during runtime when the invalid assignment is attempted.
Which Declaration Should You Use?
const
by default: Useconst
whenever you declare a variable whose value should not be reassigned after initialization. This makes your code more predictable.let
when reassignment is needed: Uselet
if you know the variable's value will need to change during its lifetime (e.g., loop counters, state variables).- Avoid
var
: Generally, avoid usingvar
in modern JavaScript due to its looser scoping rules and hoisting behavior, which can lead to unexpected bugs.let
andconst
provide block scope and the TDZ, leading to safer code.
🛡️ How to Avoid TDZ Errors
The best way to avoid ReferenceError
due to the TDZ is to always declare and initialize your let
and const
variables at the top of their scope before they are accessed.
function exampleScope() {
// Declare and initialize at the top
let count = 0;
const MAX_RETRIES = 3;
// ... rest of the code that uses count and MAX_RETRIES
if (count < MAX_RETRIES) {
// ...
count++;
}
}
🔗 Accessing Global Variables from Block Scope
If let
and const
aren't in the global scope, how can they access variables from the global scope?"
index.js | |
---|---|
Answer
This works because of Lexical Scoping and the Scope Chain.
- Lexical Scoping: JavaScript determines the scope of variables based on where they are declared in the source code (lexically). Inner scopes automatically have access to variables declared in their outer scopes.
- Scope Chain: When JavaScript needs to find a variable, it first looks in the current scope. If it doesn't find it, it looks in the immediate outer scope, then the next outer scope, and so on, all the way up to the global scope.
In the example above, when let y = x;
is executed inside checkScope
, JavaScript looks for x
within checkScope
. It doesn't find it, so it looks in the outer scope (the global scope), finds var x = 34;
, and uses that value.