What Is Hoisting In JavaScript And How It Works

JavaScript, as a dynamically-typed language, exhibits certain behaviors that might seem counterintuitive to developers. One such behavior is hoisting. In this tutorial we will delve into the details of hoisting, how it works, and its implications for developers.

Table of Contents #
  1. What Is Hoisting in JavaScript
  2. How Does Hoisting Work
  3. Example of Hoisting
  4. Variable Hoisting vs. Let and Const
  5. Function Declarations vs. Function Expressions
  6. Hoisting and the Temporal Dead Zone (TDZ)
  7. Best Practices for Avoiding Hoisting Issues

What Is Hoisting in JavaScript?

Hoisting is a JavaScript behavior where variable and function declarations are moved to the top of their containing scope during the compilation phase.

How Does Hoisting Work?

  1. In JavaScript, during the compilation phase, the JavaScript engine scans through the code to find all variable and function declarations.
  2. It allocates memory for these declarations and moves them to the top of their containing scope.
  3. While the declaration is hoisted, the assignment or initialization is left in its original position in the code.

Example of Hoisting

1. Variable Hoisting:

Let’s start by examining variable hoisting. In JavaScript, variables declared with var are hoisted to the top of their scope. However, it’s crucial to understand that only the declarations are hoisted, not the initializations.

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

In this example, the declaration var x; is hoisted to the top of the scope, but the assignment x = 5; remains in place. As a result, the first console.log(x); outputs undefined because x has been declared but not yet assigned a value.

2. Function Hoisting:

Function declarations are also hoisted in JavaScript. This means you can call a function before its actual position in the code. Here’s an example:

foo(); // Output: "Hello, I am a function"

function foo() {
    console.log('Hello, I am a function');

In this case, the entire function declaration is hoisted to the top of the scope, allowing the function to be called before its appearance in the code.

Variable Hoisting vs. Let and Const:

Variables declared with var are hoisted and initialized with undefined. But the variables declared with let or const are also hoisted, but they are not initialized. Trying to access a let or const variable before its declaration results in a ReferenceError.

console.log(y); // Output: ReferenceError: y is not defined
let y = 10;
console.log(y); // 10

Unlike var, the first console.log(y); throws a ReferenceError because the let y; declaration is hoisted to the top of the block but is not initialized until the second line.

Function Declarations vs. Function Expressions

1. Function declarations:

Function declarations are completely hoisted, including their implementation. You can use a function declared with function before its actual declaration in the code:

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

function sayHello() {
    console.log('Hello, world!');

2. Function Expressions:

Function expressions, on the other hand, are only partially hoisted. The variable that holds the function expression is hoisted, but the function implementation is not:

sayHi(); // TypeError: sayHi is not a function

var sayHi = function () {
    console.log('Hi, there!');

Hoisting and the Temporal Dead Zone (TDZ)

The Temporal Dead Zone (TDZ) is a period in which variables declared with let and const exist but cannot be accessed. During this time, referencing these variables will result in a ReferenceError.

console.log(z); // Output: ReferenceError: Cannot access 'z' before initialization
let z = 20;

This behavior is due to hoisting. The variable z is hoisted, but you can’t access it until it’s initialized.

Best Practices for Avoiding Hoisting Issues

To avoid hoisting-related problems and write more maintainable and predictable code, consider these best practices:

  • Declare your variables and functions at the top of their containing scope to make hoisting behavior more explicit.
  • Use let and const instead of var to avoid hoisting-related issues, and because they have block-level scope.
  • Avoid using a variable or function before its declaration, even though it’s allowed. This improves code readability and prevents unexpected behaviors.
  • Be aware of the differences between function declarations and function expressions, and use them appropriately.