Hoisting is a mechanism where variables and function declarations are moved to the top of their scope before code execution. It allows variables and function declarations to be used before they are declared.
console.log(name); // Alice
var name = 'Alice';
JavaScriptUnderstanding Hoisting and its impact on variable and function declarations
Javascript programs will execute in two phases.
Phase 1 : Memory Allocation/creation
Phase 2: Code Execution
During Phase 1:
Memory Allocation:
-> Javascript engine will create the global execution context and allocate memory for all variables and functions only for the global level properties.
-> All variables will get a special values called ‘undefined’, even with the arrow function
-> Complete function itself will be copied for the normal functions using function name of it.
During Phase 2:
Code Execution :
-> Javascript Engine will go line by line and execute the code.
-> Assign with actual values for those variables already had undefined.
-> When any function invoked, new execution context will be created and same 2 phases will be executed there as well. This is called ‘Hoisting’ in Javascript.
Variable Hoisting
- Variables declared using var are hoisted to the top of their scope and initialized with a value of undefined (special type)
console.log(x); // undefined
var x = 100;
console.log(x); // 100
hoistedVariable = 3
console.log(hoistedVariable); // 3
var hoistedVariable;
JavaScript- Variables declared using let and const are not hoisted to the top of their scope and are not initialized with any value. Basically this variables are in TDZ (Temporal Dead Zone)
console.log(y); // ReferenceError
let y = 100;
console.log(y); // 100
console.log(z); // ReferenceError
const z = 200;
console.log(z); // 200
JavaScriptFunction Hoisting :
We can use a function in our code before it is officially declared, making the sequence of function declarations less restrictive.
hoistedFunction(); // Hello World
function hoistedFunction() {
console.log("Hello World");
}
JavaScriptExample :
console.log(add(2,3)); // 5
function add(x, y) { // function declaration
var result = x + y
return result
}
JavaScriptExplanation: The code looks following to the JS Engine:
function add(x, y) { // function declaration
var result = x + y
return result
}
console.log(add(2,3))
JavaScriptCreation Phase:
- The JS Engine scans the code
- It recognizes the add function declaration and allocates memory for it.
- The console.log statement is also noted.
Execution Phase:
- The execution starts, and when the console.log is encountered, it sees the function add.
- Although the function is called before its declaration, the hoisting mechanism moves the function to the top during creation phase.
- The function is available, and the output is 5, the result of add(2,3).
Note to remember:
1. Variable initializations are not hoisted, only variable declaration are hoisted.
var x;
console.log(x); // undefined
x = 23;
JavaScript2. To avoid hoisting, you can run javascript in strict mode by using ‘use strict’ on top of the code:
'use strict';
x = 24; // Gives error sinxe 'x' is not declared
var x;
JavaScript3. Hoisting takes place in the local scope as well
function doSomething() {
x = 33;
console.log(x);
var x = 3;
}
doSomething(); // 3
JavaScript4. Hoisting will happen bit different with ‘var’, ‘let’ and ‘const’ types.
- “var” – this variables and functions are attached in global scope or local scope of that execution context.
- ‘let and const’ – this variables and functions are attached in the seperate memory space like (script, block, local scope) of that execution context.
We get an error, if we try to access let and const variables before initializing them as they are hoisted in a seperate space; this is also called “Temporal Dead Zone”. Read more about it here
5. The lifecycle of a variable involves 3 stages: Declaration, Initialization and Assignment.
Class Hoisting
Unlike function declarations, classes in Javascript are not hoisted. This means that you must declare a class before you can instantiate it. Attempting to use a class before its declaration results in a ReferenceError.
Example
// Attempting to instantiate a class before declaring it
const myCar = new Car('Ford'); // ReferenceError: Cannot access 'Car' before initialization
// Class declaration
class Car {
constructor(brand) {
this.brand = brand;
}
display() {
console.log(`This car is a ${this.brand}.`);
}
}
// Correct usage: Instantiating a class after it's declared
const mySecondCar = new Car('Toyota');
mySecondCar.display(); // This car is a Toyota.
JavaScriptBest practices for working with Hoisting :
While hoisting can be a powerful feature in Javascript, it’s important to follow best practices to ensure code clarity and prevent potential issues. Here are some recommended practices for working with hoisting :
- Declare variables and functions before use – Although hoisting allows you to access variables and invoke functions before their declarations, it’s considered a best practice to declare them at the beginning of their respective scopes. This improves code readability and avoids confusion.
- Use let and const for Block Scope – To minimize hoisting-related issues and ensure better scoping, prefer using let and const declarations over var. let and const have block scope and are hoisted only within their respective blocks, making the code more predictable.
- Avoid Reliance on Hoisting – While hoisting can be helpful, it’s best not to rely on it heavily for code logic. Write your code in a way that is clear and explicit, with variables and functions declared and initialized in the order they are intended to be used.
- Leverage Function Expressions – Instead of relying solely on function declarations, consider using function expressions assigned to variables. Function expressions offer more control over the variable assignment and can help prevent unintended hoisting behaviour. Function expresses and arrow functions do not undergo hoisting.
- Strict Mode: Enable strict mode
("use strict")
to enforce stricter Javascript rules. It helps catch potential hoisting-related errors and encourages better coding practices. - Code Reviews and Testing: Perform code reviews and thorough testing to identify and hoisting-related issues and ensure the behaviour of your code aligns with your intentions.
By following these best practices, one can harness the power of hoisting effectively while minimizing potential pitfalls.
Common mistakes and pitfalls with hoisting
While hoisting can be a useful feature, it’s important to be aware of common mistakes and pitfalls that can arise. Here are a few to watch out for:
- Assuming Variables are Defined: When accessing variables before their declaration, they are hoisted but will have the value
undefined
until assigned. Relying on undeclared variables can lead to unexpected behavior. - Overwriting Function Declarations: When a function is declared multiple times within the same scope, only the last declaration is effective. Avoid unintentionally overwriting functions by ensuring unique function names.
- Not Understanding Block Scope: Variables declared with
let
andconst
have block scope and are not hoisted to the entire scope. Failing to grasp this distinction can result in scope-related errors. - Misinterpreting Function Hoisting: While functions can be invoked before their declarations, function expressions assigned to variables do not exhibit the same hoisting behaviour. Be cautious with function expressions to avoid unexpected results.
By being mindful of these common pitfalls, you can navigate hoisting effectively and write more robust JavaScript code. It’s essential to understand the intricacies of hoisting to avoid potential bugs and ensure the desired behavior of your code.
Interview questions :
- What is hoisting in Javascript ? Please explain giving example.
- What are the different phases which takes place in JS Engine, while Hoisting occurs ?
- How will you avoid or prevent hoisting related issues in your code ?
- What are the different scenarios where hoisting takes place ?
Ref: