JavaScript Function Hoisting Tutorial with Examples

Function hoisting is a behavior in JavaScript where function declarations are moved, or “hoisted,” to the top of their containing scope during the compile phase before the code is executed.

This means you can call a function before it is defined in the code.

However, the behavior differs between function declarations and function expressions.

In this tutorial, we will cover:

  1. What is Hoisting?
  2. Hoisting in Function Declarations
  3. Hoisting in Function Expressions
  4. Hoisting and Arrow Functions
  5. Best Practices and Pitfalls
  6. Examples and Use Cases

Let’s dive into each section with examples.

1. What is Hoisting?

Hoisting refers to JavaScript's default behavior of moving declarations (both variable and function declarations) to the top of the current scope (global or function scope) before the code is executed.

However, only the declaration is hoisted, not the assignment.

Example: Basic Variable Hoisting

console.log(x);  // Output: undefined
var x = 5;
console.log(x);  // Output: 5

In this example, the variable x is hoisted, but the value assignment (x = 5) happens in the original place in the code. Before the assignment, x is undefined.

Similarly, function declarations are hoisted, but how they behave depends on whether they are function declarations or function expressions.

2. Hoisting in Function Declarations

A function declaration is fully hoisted, which means both the function name and its body are hoisted to the top of the current scope. This allows you to call the function before it is defined in the code.

Example: Function Declaration Hoisting

greet();  // Output: Hello, World!

// Function declaration
function greet() {
  console.log("Hello, World!");
}

In this example:

  • The greet() function can be called before it is defined because both the function's name and body are hoisted to the top of the scope during the compile phase.
  • Function declarations are fully hoisted and can be called anywhere in their scope.

How Hoisting Works in Function Declarations

When the JavaScript engine processes the code, the function declaration is “moved” to the top. Internally, it looks like this:

// JavaScript internally does this during hoisting
function greet() {
  console.log("Hello, World!");
}

greet();  // Output: Hello, World!

3. Hoisting in Function Expressions

A function expression is when a function is assigned to a variable. Unlike function declarations, only the variable is hoisted, not the function definition. This means you cannot call a function expression before it is defined.

Example: Function Expression Hoisting

console.log(sum);  // Output: undefined
// sum(5, 10);  // This will throw an error: TypeError: sum is not a function

// Function expression
var sum = function(a, b) {
  return a + b;
};

console.log(sum(5, 10));  // Output: 15

In this example:

  • The variable sum is hoisted, but it is initialized with undefined until the function expression is assigned. Therefore, calling sum() before the function expression will result in an error because sum is undefined.

How Hoisting Works in Function Expressions

When the JavaScript engine processes the code, it looks like this:

var sum;

console.log(sum);  // Output: undefined
// sum(5, 10);  // Error: sum is not a function

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

console.log(sum(5, 10));  // Output: 15

4. Hoisting and Arrow Functions

Arrow functions are a concise syntax for writing functions in JavaScript. They are treated similarly to function expressions in terms of hoisting. This means arrow functions are not hoisted, and attempting to use them before their definition will result in an error.

Example: Arrow Function Hoisting

// console.log(add(5, 3));  // This will throw an error: TypeError: add is not a function

// Arrow function
var add = (a, b) => a + b;

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

In this example:

  • The arrow function add is assigned to a variable, so only the variable add is hoisted (with the value undefined). This causes an error if the function is called before it is defined.

How Hoisting Works with Arrow Functions

When the JavaScript engine processes the code, it looks like this:

var add;

// console.log(add(5, 3));  // Error: add is not a function

add = (a, b) => a + b;

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

5. Best Practices and Pitfalls

Best Practices:

  • Declare functions before using them: Even though function declarations are hoisted, it is generally a good practice to define your functions before using them. This makes the code more readable and predictable.
    function greet() {
      console.log("Hello!");
    }
    
    greet();
    
  • Avoid hoisting pitfalls with function expressions: Since function expressions are not fully hoisted, avoid calling them before they are defined. Always define them before usage.
    const multiply = (a, b) => a * b;
    console.log(multiply(5, 10));  // Output: 50
    

Pitfalls:

  • Calling function expressions or arrow functions before declaration: Trying to call a function expression or arrow function before it is defined will result in a runtime error because the function body is not hoisted, only the variable is.

6. Examples and Use Cases

Example 1: Function Declaration and Expression Comparison

// Function Declaration
sayHello();  // Output: Hello, User!

function sayHello() {
  console.log("Hello, User!");
}

// Function Expression
// greetUser();  // This will throw an error: TypeError: greetUser is not a function
var greetUser = function() {
  console.log("Greetings, User!");
};

greetUser();  // Output: Greetings, User!

In this example:

  • The function sayHello() is hoisted, so it can be called before it is defined.
  • The function expression greetUser is not hoisted, so it must be defined before calling it.

Example 2: Hoisting with Arrow Functions

// Arrow Function
// console.log(double(4));  // This will throw an error: TypeError: double is not a function

var double = (x) => x * 2;

console.log(double(4));  // Output: 8

In this example:

  • The arrow function double is not hoisted, so you must define it before calling it.

Example 3: Variable Hoisting with Function Declarations

console.log(myVar);  // Output: undefined

// Function Declaration
function myFunction() {
  console.log("Function called");
}

// Variable Declaration
var myVar = 10;

myFunction();  // Output: Function called

In this example:

  • The variable myVar is hoisted with the value undefined, while the function myFunction() is fully hoisted and can be called before it is defined.

Summary of Key Differences in Hoisting

Feature Function Declarations Function Expressions Arrow Functions
Hoisted Yes, fully hoisted (name and body). Only the variable is hoisted, not the function body. Only the variable is hoisted, not the function body.
Called before definition Yes, can be called before definition. No, cannot be called before definition. No, cannot be called before definition.
Example function greet() {} var greet = function() {}; var greet = () => {};

Conclusion

In JavaScript, function hoisting allows function declarations to be fully hoisted to the top of their scope, making them callable before they are defined.

However, function expressions and arrow functions are only partially hoisted, with the variable being hoisted but not the function body.

In this tutorial, we covered:

  • Hoisting in function declarations, which allows you to call functions before their definition.
  • Hoisting in function expressions and arrow functions, where the function is not hoisted and must be defined before being called.
  • Best practices for avoiding common hoisting pitfalls.

Related posts

JavaScript Self-Invoking Functions Tutorial with Examples

JavaScript Callbacks: A Complete Tutorial

JavaScript Arrow Functions: A Complete Tutorial