Language: EN

javascript-hoisting

Hoisting in JavaScript

In JavaScript, Hoisting is a behavior of the JavaScript engine that affects how variable and function declarations are interpreted.

Hoisting causes variable and function declarations to be “elevated” to the beginning of their execution context.

By “elevate” the declaration, we mean that the interpreter knows that a function exists, even if we declare it later in the code.

Hoisting is a very useful feature that makes development easier. However, it is also one of the least understood and most confusing concepts.

javascript-hoisting

So let’s take a look at it 👇

What is Hoisting Used For

As I mentioned, Hoisting means that the interpreter knows the variables or functions before their declaration. In other words, we can do this.

greet(); // Output: "Hello, world!"

function greet() {
    console.log("Hello, world!");
}

In this case,

  • The function greet can be called before its declaration
  • This is because Hoisting “elevates” the declaration.

It may sound a bit strange when read like this, but it makes a lot of sense. The purpose of Hoisting is to simplify our lives when writing code.

If we didn’t have Hoisting, we would have to write functions in the same order we use them.

function function1() {
    console.log("Hello, world!");
}

function function2() {
	function1();
}

Which may seem like a small problem. But as you write code, you would have to constantly change the order of functions (to be in the same order that you use them).

This would be a real hassle 🙅. So the interpreter first goes through the entire file collecting the definitions to make it easier for us.

JavaScript is not the only language that has this “problem.” Most modern languages do not require maintaining order.

Others do require it, such as C++. So they need other mechanisms like .header files or forward declarations, which are even less intuitive.

How Hoisting Works

Function Hoisting

Functions in JavaScript are fully elevated, both the declaration and the implementation are elevated to the beginning of the execution context.

This means you can invoke them before their declaration. Let’s see an example:

console.log(add(5, 3)); // 8

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

Here, the call to add is made before the function declaration

Variable Hoisting

Variable declarations using let and const are also elevated. However, they are not available before their actual declaration.

This is because the interpreter places them in what we call TDZ (temporal dead zone) until their declaration is reached.

console.log(myLetVariable); 
// ReferenceError: Cannot access 'myLetVariable' before initialization

let myLetVariable = 10;

In this case, the code throws a ReferenceError because myLetVariable is not defined before its declaration.

It is often said that the definition is elevated, but not the initialization. It doesn’t matter much; the important thing is that the TDZ will not allow you to access it.

Example: Variable Containing a Function

Let’s look at an example that may generate some confusion. What happens if we have a variable that contains a function?

Well, a variable that contains a function is still a variable. So it will follow the rules we have seen regarding variable behavior.

Let’s assume this case,

console.log(multiply(5, 3)); 
// ReferenceError: Cannot access 'multiply' before initialization

let multiply = function(a, b) {
    return a * b;
};

In this case,

  • A ReferenceError occurs
  • The error occurs because the variable multiply is elevated, but the assignment of the function to multiply is not.
  • Therefore, when trying to call multiply before the assignment, an error is thrown because it is in its TDZ
  • Exactly the same as with a variable containing any other value