What is Hoisting in JavaScript?
Hoisting is JavaScript's default behavior of moving declarations to the top of the current scope (script or function).
This means that var
and function declarations are "hoisted" to the top of their scope before code execution begins. However, only the declarations are hoisted, not the initializations.
Hoisting with var
Let’s begin with a simple example using var
:
console.log(name); // undefined
var name = "John";
console.log(name); // John
Output:
undefined John
Explanation: During the compilation phase, JavaScript sees the var name
declaration and hoists it to the top, but the value "John"
is not assigned until that line is executed. So initially, name
is undefined
.
Question: Why doesn't it throw a ReferenceError?
Because var
declarations are hoisted and initialized with undefined
. So the variable name
exists, just with a default value of undefined
before it’s explicitly assigned.
Hoisting with let
and const
Variables declared with let
and const
are also hoisted, but not initialized. Accessing them before declaration results in a ReferenceError due to the "Temporal Dead Zone (TDZ)".
console.log(age); // ReferenceError: Cannot access 'age' before initialization
let age = 25;
Output:
ReferenceError: Cannot access 'age' before initialization
Explanation: JavaScript knows that age
is declared, but it is in the Temporal Dead Zone – the period between hoisting and actual declaration where the variable cannot be accessed.
Function Hoisting
Function declarations are fully hoisted – both their name and body. This means you can call a function even before it's defined in the code:
greet();
function greet() {
console.log("Hello, world!");
}
Output:
Hello, world!
Explanation: Since the entire function greet()
is hoisted, it can be called before its definition in the code.
Function Expressions are Not Hoisted
If a function is assigned to a variable (a function expression), only the variable is hoisted, not the function definition:
sayHi(); // TypeError: sayHi is not a function
var sayHi = function() {
console.log("Hi!");
};
Output:
TypeError: sayHi is not a function
Explanation: Here, sayHi
is hoisted as a variable with an initial value of undefined
. When you try to call it, it's not a function yet, so it throws a TypeError
.
Best Practices
- Always declare variables at the top of their scope to avoid confusion caused by hoisting.
- Prefer
let
andconst
overvar
to prevent unintentional hoisting bugs. - Avoid using functions before declaring them, unless they are function declarations.
Question: How does hoisting affect block scope?
Hoisting happens inside function or global scope. If you declare variables using var
inside a block (e.g., if or for), they still get hoisted to the function scope, not the block.
if (true) {
var test = "block";
}
console.log(test); // block
Output:
block
But if you use let
or const
, the variable is truly block-scoped:
if (true) {
let value = "block";
}
console.log(value); // ReferenceError: value is not defined
Output:
ReferenceError: value is not defined