
JavaScript Hoisting: The Complete Developer's Guide to Understanding Variable and Function Behavior
In this article, we will learn what is hoisting in javascript and how actually it works in the real world.
What is Hoisting?
Just a few microseconds before your code is run, during the compilation phase, it is checked for function and variable declarations.
All these functions and variable declarations are stored in memory inside a JavaScript data structure called Lexical Environment.
So that they can be used even before they are actually declared in the source code.
What is a Lexical Environment?
A lexical environment is a data structure that holds an identifier-variable mapping. (identifier refers to the name of variables/functions, and the variable is the reference to the actual object [including function object] or primitive value).
So, in other words, a lexical environment is a place where variables and functions live during the program execution.
Function Hoisting:
In the Creation phase, all declared functions are moved to the top of their scope
sum(15, 10);
function sum(a, b) {
add = a + b;
console.log(add);
}
//Output: '25' to the console
So in the above code, that function declaration is added to the memory during the compile stage, so we are able to access it in our code before the actual function declaration.
Exception of Hoisting:
Only function declarations are hoisted in JavaScript, function expressions are not hoisted.
For example: this code won’t work.
helloWorld(); // TypeError: helloWorld is not a function
var helloWorld = function(){
console.log('Hello World!');
}
As JavaScript only hoist declarations, not initializations (assignments), so the helloWorld
will be treated as a variable, not as a function. Because helloWorld
is a var
variable, so the engine will assign is the undefined
the value during hoisting.
Variable Hoisting:
Now let's see how it works with variables, see below example:
console.log(sample);
var sample = 7;
// outputs 'undefined'
So, why we get undefined instead of 7?
Because, JavaScript only hoists declarations, not initializations.
JavaScript only keeps the declarations of functions and variables in memory during compilation time, not their assignments (value).
How undefined is set?
The var variable will be added to the lexical environment and initialised with undefined when the JavaScript engine discovers a var variable declaration during the compilation phase.
Later, during execution, when it reaches the line where the actual assignment is made in the code, it will assign that value to the variable.
That’s why we got undefined
instead of 7
Hoisting let and const variables:
See the below example:
console.log(7);
let sample = 7;
//Output : ReferenceError: sample is not defined
Why we got this error?
Because,All declarations (function, var, let and const) are hoisted in JavaScript, while the var declarations are initialized with undefined, but let and const declarations remain uninitialized.
They won't be initialised until the JavaScript engine evaluates their lexical binding (assignment) during runtime.
This means you can’t access the variable before the engine evaluates its value at the place it was declared in the source code.
This scenario is also called a "Temporal Dead Zone", a period of time between the creation of a variable and its initialization during which it cannot be accessed.
Conclusion
So, now we saw how exactly Hosting works in JavaScript, which will assist you in preventing any future hoisting-related issues and misunderstandings.
Always try to declare variables at the top of their respective scopes and always try to initialize variables when you declare them to prevent potential hoisting side effects like undefined variables or reference errors.
Have you ever wondered why some JavaScript code works even when variables are used before they're declared? Or why certain functions can be called before their definition? The answer lies in one of JavaScript's most fascinating concepts: hoisting.
Understanding JavaScript hoisting behavior is crucial for writing predictable, bug-free code. In this comprehensive guide, we'll explore how JavaScript hoisting works, common pitfalls, and real-world examples that will make you a more confident developer.
What is JavaScript Hoisting and Why Does It Matter?
JavaScript hoisting is the engine's default behavior of moving variable and function declarations to the top of their containing scope during the compilation phase. This means that regardless of where you declare your variables and functions in your code, they're conceptually "hoisted" to the beginning of their scope.
Think of hoisting like a helpful assistant who reads through your entire code file first, takes note of all your variable and function declarations, and then processes them before executing any other code.
The JavaScript Engine's Two-Phase Process
JavaScript execution happens in two phases:
- Compilation Phase: The engine scans for declarations and allocates memory
- Execution Phase: The engine runs your code line by line
During the compilation phase, declarations are hoisted, but initializations remain in their original position.
How JavaScript Variable Hoisting Works: var vs let vs const
Variable Hoisting with var Keyword
Variables declared with var
are hoisted and initialized with undefined
:
console.log(myName); // undefined (not an error!)
var myName = "John";
console.log(myName); // "John"
Behind the scenes, JavaScript interprets this as:
var myName; // hoisted to the top
console.log(myName); // undefined
myName = "John";
console.log(myName); // "John"
Let and Const Hoisting Behavior (Temporal Dead Zone)
Variables declared with let
and const
are also hoisted, but they remain uninitialized until their declaration is reached:
console.log(age); // ReferenceError: Cannot access 'age' before initialization
let age = 25;
This creates what's called the Temporal Dead Zone - the period between the start of the scope and the variable's declaration.
Real-World Example: Shopping Cart Implementation
Here's a practical example showing how hoisting affects a shopping cart:
// This works due to hoisting
function calculateTotal() {
console.log(discount); // undefined (not an error)
if (isVIP) {
var discount = 0.2;
} else {
var discount = 0.1;
}
return basePrice * (1 - discount);
}
var isVIP = true;
var basePrice = 100;
Better approach using let/const:
function calculateTotal() {
let discount;
if (isVIP) {
discount = 0.2;
} else {
discount = 0.1;
}
return basePrice * (1 - discount);
}
const isVIP = true;
const basePrice = 100;
JavaScript Function Hoisting: Declaration vs Expression
Function Declaration Hoisting
Function declarations are completely hoisted, meaning you can call them before they're defined:
// This works perfectly
greetUser("Alice"); // "Hello, Alice!"
function greetUser(name) {
return `Hello, ${name}!`;
}
Function Expression Hoisting Behavior
Function expressions behave differently based on how they're declared:
// This throws an error
sayHello(); // TypeError: sayHello is not a function
var sayHello = function() {
return "Hello!";
};
Arrow Functions and Hoisting
Arrow functions follow the same hoisting rules as the variables they're assigned to:
// This won't work
multiply(5, 3); // TypeError: multiply is not a function
const multiply = (a, b) => a * b;
Best Practices for JavaScript Hoisting in Modern Development
1. Use const and let Instead of var
Modern JavaScript development favors const
and let
for better scoping and to avoid hoisting-related bugs:
// Good practice
const API_URL = "https://api.example.com";
let userData = null;
// Avoid
var apiUrl = "https://api.example.com";
var userData;
2. Declare Variables at the Top of Their Scope
Make hoisting explicit by declaring variables at the beginning of their scope:
function fetchUserData(userId) {
let userData;
let error;
try {
userData = api.getUser(userId);
} catch (err) {
error = err.message;
}
return { userData, error };
}
Debugging JavaScript Hoisting Issues
Using Developer Tools
Modern browsers provide excellent debugging tools for hoisting issues:
- Use breakpoints to inspect variable values at different execution points
- Check the scope panel to see hoisted variables
- Use
console.log()
strategically to trace variable initialization
Common Error Messages and Solutions
Error Message | Cause | Solution |
---|---|---|
ReferenceError: Cannot access 'variable' before initialization |
Accessing let/const before declaration | Move usage after declaration |
TypeError: variable is not a function |
Calling function expression before initialization | Use function declaration or move call |
undefined instead of expected value |
var hoisting with conditional assignment | Use let/const with proper initialization |
JavaScript Hoisting Interview Questions and Answers
Question 1: What will this code output?
console.log(a);
var a = 1;
console.log(a);
Answer: undefined
then 1
. The variable a
is hoisted and initialized with undefined
.
Question 2: Will this code work?
foo();
function foo() {
console.log('Hello');
}
Answer: Yes, it will print "Hello". Function declarations are completely hoisted.
Question 3: What happens here?
console.log(x);
let x = 5;
Answer: ReferenceError: Cannot access 'x' before initialization
. The variable is in the temporal dead zone.
Frequently Asked Questions About JavaScript Hoisting
What is the difference between hoisting in var, let, and const?
var: Hoisted and initialized with undefined
let: Hoisted but uninitialized (temporal dead zone)
const: Hoisted but uninitialized (temporal dead zone)
Are arrow functions hoisted in JavaScript?
No, arrow functions are not hoisted because they're assigned to variables. They follow the hoisting rules of the variable declaration type used (var
, let
, or const
).
Does hoisting happen in strict mode?
Yes, hoisting occurs in strict mode, but strict mode adds additional restrictions that can help prevent hoisting-related bugs.
How does hoisting work with nested functions?
Nested functions follow the same hoisting rules within their containing scope. Each function creates its own hoisting context.
Can hoisting cause memory leaks?
Hoisting itself doesn't cause memory leaks, but poor understanding of hoisting can lead to unintended variable retention and scope pollution.
What is the temporal dead zone in JavaScript?
The temporal dead zone is the period between the start of a scope and the actual declaration of a let
or const
variable, during which the variable cannot be accessed.
How can I avoid hoisting-related bugs?
- Use
const
andlet
instead ofvar
- Declare variables at the top of their scope
- Use ESLint rules to catch hoisting issues
- Write comprehensive tests for your functions
Conclusion: Mastering JavaScript Hoisting
Understanding JavaScript hoisting is essential for writing predictable, maintainable code. By following modern best practices like using const
and let
, declaring variables at the top of their scope, and being aware of the temporal dead zone, you can avoid common pitfalls and write more robust JavaScript applications.
Remember that hoisting is a feature that can be both helpful and harmful. Use it wisely, and always prioritize code clarity and maintainability over clever hoisting tricks.
Related Resources: