Hoisting in JavaScript

Hoisting in JavaScript

In this article, we'll look into what hoisting in JavaScript really is.

What is hoisting?

Hoisting is a concept or behavior that exists in languages such as JavaScript, where function and variable declarations are moved to the top of their scope before code execution.

Consider the example below:

function1();
function2();

function function1(){ 
    console.log("I am the first function");
}

function function2(){ 
    console.log("I am the second function");
}

Why does the above code work without any error, especially since the functions have been declared after their execution? Hoisting. We'll dive deeper on the concept and look at how hoisting works in functions, classes and variables.

Hoisting in Functions

In JavaScript, code runs from top to bottom. So in a case such as the function shown below, we would expect to return 3, since the function is executed just after its declaration.

function sum(a,b) {
    return a + b;
}

console.log(sum(1,2)); // return 3

Let's look at another example.

console.log(sum(1,2)); // returns 3

function sum(a,b) {
    return a + b;
}

Here we execute the function before declaring it. In hoisting, the function becomes accessible even before its declaration since the function is raised (hoisted) to the top of the scope in which it was defined.

Hoisting, however, would work in functions declared using the function keyword and not the const keyword. Hoisting would not work in an arrow function such as the one shown below, and would instead throw a ReferenceError.

console.log(sum(1,2)); 
// ReferenceError: Cannot access 'sum' before initialization

const sum = (a,b) => {
    return a + b;
}

Hoisting in Variables

In JavaScript, variables can be declared using var, let or const. Hoisting behaves differently based on the variable declaration.

Let's consider the example below:

console.log(num); //returns undefined

var num = 2;

console.log(num); //returns 2

The variable num is accessed before declaring it. By default, due to hoisting, the variable is hoisted to the top and is initialized with a default value of undefined. The variable is initialized with its actual value after its declaration.

Hoisting in let and const has a different behavior compared to var. Consider the example below:

console.log(num); 
//returns a ReferenceError: Cannot access 'num' before initialization

let num = 2;

Variables declared using let and const throw a ReferenceError since they do not get initialized with a default value of undefined as is seen in var declarations. They do get hoisted to the top of the scope but are not accessible, thus the error message explains that it Cannot access 'num' before initialization. This concept is known as Temporal Dead Zone(TDZ). Let and const variables can only be accessed after declaring them.

Hoisting in Classes

Consider the following example:

const Book1 = new Book("Lord of the Rings");
//returns ReferenceError: Cannot access 'Book' before initialization

class Book {
  constructor(title) {
    this.title = title;
  }
}

Just like in let and const variables, classes are hoisted to the top of the scope but are inaccessible until they are initialized. The interpreter 'knows' of the existence of the Book class, but the class cannot be accessed until its initialization.

class Book {
  constructor(title) {
    this.title = title;
  }
}

const Book1 = new Book("Lord of the Rings");
console.log(Book1); //returns Book { title: 'Lord of the Rings' }

The Book class can only be accessible for instantiation after the class has been initialized.

Wrap Up

JavaScript hoisting involves the process where the interpreter seems to move the declaration of functions and variables to the top of their scope during the compilation phase. This behavior can be quite confusing and can sometimes lead to unexpected results if not properly understood, but with a clear understanding of how hoisting works, developers can leverage hoisting to their advantage and write more organized and maintainable code.