TechCamp Pro
What exactly happens when JavaScript accesses a variable before its declaration? Learn how hoisting works with `var`, `let`, and `const`, including the Memory Creation Phase and Temporal Dead Zone with practical examples.
Muhammad Ali
Full Stack Developer

Hoisting is a behavior where variable and function declarations are moved to the top of their scope during the compile phase.
Occasional notes on code, craft, and things I break along the way. No spam — unsubscribe anytime.
Written by
Muhammad Ali
Full Stack Developer
What is ECMAScript and how is it different from JavaScript? Learn the history of ES1, ES2, ES3, ES5, ES6, ES2016–ES2026 and the major features introduced in each version.
Muhammad Ali
Full Stack Developer
FeaturedTutorialsLearn JavaScript closures from scratch with simple examples. Understand lexical scope, how closures work, data privacy, callbacks, setTimeout, and common interview questions.
Muhammad Ali
Full Stack Developer
TutorialsLearn the differences between Map, Set, WeakMap, and WeakSet in JavaScript. Understand their syntax, use cases, key differences, memory behavior, and when to use each.
Muhammad Ali
Full Stack Developer
var vs let vs constHave you ever wondered why this JavaScript code works?
console.log(name);
var name = "Ali";
The output is:
undefined
But when you replace var with let:
console.log(name);
let name = "Ali";
You get:
ReferenceError
Why does this happen?
The answer is JavaScript Hoisting.
Hoisting is one of the most commonly asked JavaScript interview topics. It becomes much easier to understand once you know how JavaScript creates an execution environment before running your code.
In this article, we'll understand:
var vs let vs constHoisting is the behavior where JavaScript processes declarations before executing the code in their scope.
This means JavaScript knows about certain variables and functions before reaching their declaration during normal top-to-bottom execution.
For example:
console.log(message);
var message = "Hello";
JavaScript effectively treats the declaration as if it were processed before the execution of the console.log():
var message;
console.log(message);
message = "Hello";
Therefore, the result is:
undefined
Important: Hoisting does not literally move your code to the top of the file. It describes how declarations are handled when JavaScript creates the execution environment.
To understand hoisting properly, you need to understand the two broad stages of execution.
For a deeper understanding, read our guide on JavaScript Execution Context.
Before executing the statements, JavaScript creates the necessary execution environment and processes declarations.
For example:
var name = "Ali";
function greet() {
console.log("Hello");
}
During this stage, JavaScript knows about:
name → undefined
greet → function
The function declaration is available as a function, while the var binding is initialized to undefined.
JavaScript then executes the code according to its normal execution order.
name = "Ali";
greet();
Now:
name → "Ali"
This creation-then-execution model explains many hoisting behaviors.
var HoistingLet's start with var.
console.log(age);
var age = 25;
Output:
undefined
Why?
During the setup of the execution environment, the var binding is initialized with undefined.
Conceptually:
var age;
console.log(age);
age = 25;
So JavaScript doesn't throw a ReferenceError here.
var Before DeclarationConsider this:
console.log(a);
console.log(b);
var a = 10;
var b = 20;
Output:
undefined
undefined
Both variables are known before their assignment happens.
let and HoistingNow let's look at let.
console.log(age);
let age = 25;
This results in:
ReferenceError
At first, this can seem confusing.
If let wasn't hoisted, why does JavaScript know that age exists?
The important detail is that let declarations are processed when the lexical environment is created, but they are not initialized to undefined in the same way as var.
They remain uninitialized until execution reaches their declaration.
The period between entering a scope and reaching the declaration of a let or const variable is called the Temporal Dead Zone.
Example:
console.log(age);
let age = 25;
The TDZ exists here:
┌──────────────────────────────┐
│ Temporal Dead Zone │
│ │
│ console.log(age); ❌ │
│ │
├──────────────────────────────┤
│ let age = 25; ✅ │
└──────────────────────────────┘
You cannot access age while it is in the TDZ.
After the declaration executes:
let age = 25;
console.log(age);
Output:
25
const and Hoistingconst behaves similarly to let.
console.log(name);
const name = "Ali";
Result:
ReferenceError
The variable is unavailable during the Temporal Dead Zone.
Once the declaration is reached, the variable is initialized.
const name = "Ali";
console.log(name);
Output:
Ali
var vs let vs constHere's the important comparison:
| Feature | var | let | const |
|---|---|---|---|
| Declaration processed before execution | Yes | Yes | Yes |
| Initialized during setup | undefined | No | No |
| TDZ | No | Yes | Yes |
| Can be reassigned | Yes | Yes | No |
| Block scoped | No | Yes | Yes |
| Function scoped | Yes | No | No |
The biggest difference related to hoisting is initialization.
Consider:
console.log(a);
console.log(b);
console.log(c);
var a = 10;
let b = 20;
const c = 30;
What happens?
undefined
ReferenceError
Execution stops when JavaScript tries to access b while it is still in the TDZ.
The var variable has already been initialized with undefined.
let Throw a ReferenceError?This is a common interview question.
The answer is:
letis processed before execution, but it remains uninitialized until the declaration is reached. Accessing it before initialization occurs inside the Temporal Dead Zone and results in aReferenceError.
Function declarations are also hoisted.
Consider:
greet();
function greet() {
console.log("Hello!");
}
Output:
Hello!
The function can be called before its declaration because the function declaration is available when the execution environment is prepared.
This is where things get interesting.
greet();
function greet() {
console.log("Hello");
}
Works successfully.
vargreet();
var greet = function () {
console.log("Hello");
};
This results in:
TypeError: greet is not a function
Why?
Conceptually:
var greet;
greet();
greet = function () {
console.log("Hello");
};
greet is initially undefined, so JavaScript cannot call it as a function.
letgreet();
let greet = function () {
console.log("Hello");
};
This produces:
ReferenceError
because greet is still inside the Temporal Dead Zone.
varLet's visualize it:
console.log(x);
var x = 100;
Conceptually:
Creation:
x → undefined
Execution:
console.log(x) → undefined
x = 100
letconsole.log(x);
let x = 100;
Conceptually:
Creation:
x → uninitialized
Execution:
console.log(x) → ReferenceError
let x = 100
constconsole.log(x);
const x = 100;
Conceptually:
Creation:
x → uninitialized
Execution:
console.log(x) → ReferenceError
const x = 100
No.
This is one of the most common misconceptions about hoisting.
JavaScript does not physically move your declarations to the beginning of the source code.
Instead, the JavaScript engine creates the relevant execution environment and processes declarations before executing the statements.
So this:
console.log(name);
var name = "Ali";
should not be understood as JavaScript literally moving:
var name;
to the top.
It is better to think about it in terms of declaration processing and initialization.
Modern JavaScript development generally favors let and const over var.
const by defaultconst name = "Ali";
let when reassignment is requiredlet count = 0;
count++;
varvar name = "Ali";
let and const gives you block scoping and makes accidental access before initialization easier to catch.What will this code output?
console.log(a);
var a = 10;
console.log(b);
let b = 20;
undefined
ReferenceError
The first variable is var, so it is initialized to undefined.
The second variable is let, so accessing it before its declaration executes causes a ReferenceError.
What happens here?
sayHello();
function sayHello() {
console.log("Hello");
}
Hello
The function declaration is available before normal execution reaches its position.
var is initialized to undefined.let and const remain uninitialized until execution reaches their declarations.let and const have a Temporal Dead Zone.var is function-scoped.let and const are block-scoped.const and let over var.JavaScript hoisting can look confusing at first, especially when comparing var, let, and const.
The easiest way to understand it is to remember one key idea:
JavaScript processes declarations before executing the code, but different declarations are initialized differently.
var is initialized with undefined, while let and const remain uninitialized and are protected by the Temporal Dead Zone.
Once you understand this behavior, many JavaScript concepts that previously seemed mysterious become much easier to understand.