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:
- What is Hoisting?
- Hoisting in Function Declarations
- Hoisting in Function Expressions
- Hoisting and Arrow Functions
- Best Practices and Pitfalls
- Examples and Use Cases
Let’s dive into each section with examples.
Table of Contents
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.