Understanding Hoisting in JavaScript: Why Can We Use `var` Before Declaration?

Hoisting is a fundamental concept in JavaScript that often confuses beginners. You might have seen situations where a variable declared using var is accessed even before it is defined in the code, without throwing an error. How does that work? To understand this, let's dive into hoisting, the JavaScript execution context, and how the JavaScript engine processes code. What is Hoisting? Hoisting is JavaScript’s default behavior of moving variable and function declarations to the top of their scope before execution. This means JavaScript allocates memory for variables and functions during the creation phase of execution, making them available before the code actually runs. But there’s an important distinction: Function declarations are hoisted with their definitions, meaning you can call a function before declaring it. Variables declared with var are hoisted but initialized with undefined, meaning you can reference them before declaration, but they won't hold their actual value until the assignment line is executed. let and const variables are hoisted but not initialized, so accessing them before declaration results in a ReferenceError. The JavaScript Execution Context and Hoisting If don't know what is JavaScript Execution Context or doesn't have a full understanding of it read my this article : Behind the Scenes of JavaScript: How Execution Works with Execution Context & Call Stack JavaScript executes code in two phases inside the Execution Context: Creation Phase (Memory Allocation) The JavaScript engine scans the code and sets up the execution context. It allocates memory for variables and functions. Functions are stored with their full definitions. Variables declared with var are set to undefined. let and const variables are stored but are not initialized, causing the Temporal Dead Zone (TDZ) error if accessed before declaration. Execution Phase (Code Execution) JavaScript runs the code line by line. Variable values get assigned during execution. Function calls are executed where they appear. Example: Hoisting with var console.log(x); // Output: undefined var x = 10; console.log(x); // Output: 10 Behind the Scenes (How JavaScript Executes It) During the Creation Phase, JavaScript sees var x = 10; and does the following: Declares x and assigns it undefined in memory. The console.log(x); at the top executes, printing undefined. When the execution reaches x = 10;, it assigns 10 to x. The second console.log(x); prints 10. The hoisting behavior explains why console.log(x) doesn’t throw an error but prints undefined instead. Why Doesn’t let and const Work the Same Way? console.log(y); // ReferenceError: Cannot access 'y' before initialization let y = 20; Here, y is hoisted, but it remains uninitialized in a Temporal Dead Zone (TDZ) until the execution reaches let y = 20;. This protects developers from accidental errors by ensuring variables cannot be accessed before assignment. Hoisting with Functions Unlike var, function declarations are fully hoisted with their definitions. greet(); // Output: Hello, World! function greet() { console.log("Hello, World!"); } JavaScript hoists the entire function definition, so calling greet() before its declaration works perfectly. Notes: Hoisting moves variable and function declarations to the top of their scope before execution. var variables are hoisted and initialized as undefined, allowing access before declaration. let and const are hoisted but remain uninitialized, causing a ReferenceError if accessed before declaration. Function declarations are fully hoisted, meaning they can be called before being defined. Hoisting helps JavaScript interpret code efficiently but can lead to unexpected behavior if not understood properly. Best Practices to Avoid Hoisting Issues Always declare variables at the beginning of their scope. Prefer let and const over var to avoid unintended undefined values. Declare and initialize variables at the same time to avoid unexpected results. Use arrow function (const func = () => {}) instead of function declarations if you don’t want them to be hoisted.

Mar 25, 2025 - 20:48
 0
Understanding Hoisting in JavaScript: Why Can We Use `var` Before Declaration?

Hoisting is a fundamental concept in JavaScript that often confuses beginners. You might have seen situations where a variable declared using var is accessed even before it is defined in the code, without throwing an error. How does that work?

To understand this, let's dive into hoisting, the JavaScript execution context, and how the JavaScript engine processes code.

What is Hoisting?

Hoisting is JavaScript’s default behavior of moving variable and function declarations to the top of their scope before execution. This means JavaScript allocates memory for variables and functions during the creation phase of execution, making them available before the code actually runs.

But there’s an important distinction:

  1. Function declarations are hoisted with their definitions, meaning you can call a function before declaring it.
  2. Variables declared with var are hoisted but initialized with undefined, meaning you can reference them before declaration, but they won't hold their actual value until the assignment line is executed.
  3. let and const variables are hoisted but not initialized, so accessing them before declaration results in a ReferenceError.

The JavaScript Execution Context and Hoisting

If don't know what is JavaScript Execution Context or doesn't have a full understanding of it read my this article :

Behind the Scenes of JavaScript: How Execution Works with Execution Context & Call Stack

JavaScript executes code in two phases inside the Execution Context:

  1. Creation Phase (Memory Allocation)
  • The JavaScript engine scans the code and sets up the execution context.
  • It allocates memory for variables and functions.
  • Functions are stored with their full definitions.
  • Variables declared with var are set to undefined.
  • let and const variables are stored but are not initialized, causing the Temporal Dead Zone (TDZ) error if accessed before declaration.
  1. Execution Phase (Code Execution)
  • JavaScript runs the code line by line.
  • Variable values get assigned during execution.
  • Function calls are executed where they appear.

Example: Hoisting with var

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

Behind the Scenes (How JavaScript Executes It)

During the Creation Phase, JavaScript sees var x = 10; and does the following:

  • Declares x and assigns it undefined in memory.
  • The console.log(x); at the top executes, printing undefined.
  • When the execution reaches x = 10;, it assigns 10 to x.
  • The second console.log(x); prints 10.

The hoisting behavior explains why console.log(x) doesn’t throw an error but prints undefined instead.

Why Doesn’t let and const Work the Same Way?

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

Here, y is hoisted, but it remains uninitialized in a Temporal Dead Zone (TDZ) until the execution reaches let y = 20;. This protects developers from accidental errors by ensuring variables cannot be accessed before assignment.

Hoisting with Functions

Unlike var, function declarations are fully hoisted with their definitions.

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

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

JavaScript hoists the entire function definition, so calling greet() before its declaration works perfectly.

Notes:

  • Hoisting moves variable and function declarations to the top of their scope before execution.
  • var variables are hoisted and initialized as undefined, allowing access before declaration.
  • let and const are hoisted but remain uninitialized, causing a ReferenceError if accessed before declaration.
  • Function declarations are fully hoisted, meaning they can be called before being defined.
  • Hoisting helps JavaScript interpret code efficiently but can lead to unexpected behavior if not understood properly.

Best Practices to Avoid Hoisting Issues

  • Always declare variables at the beginning of their scope.
  • Prefer let and const over var to avoid unintended undefined values.
  • Declare and initialize variables at the same time to avoid unexpected results.
  • Use arrow function (const func = () => {}) instead of function declarations if you don’t want them to be hoisted.