Hoisting


Hoisting: Understanding JavaScript's Peculiar Behavior with var and function Declarations

Hoisting is a fundamental JavaScript mechanism where var declarations and function declarations are moved to the top of their containing scope before code execution. This means you can use a variable or function before it's physically declared in your code, which can sometimes lead to unexpected results. Understanding hoisting is key to writing predictable and bug-free JavaScript.


Example 1: Basic var Hoisting

// Example of basic variable hoisting
console.log(myVar); // Outputs: undefined
var myVar = "Hello, JavaScript!";
console.log(myVar); // Outputs: "Hello, JavaScript!"

Explanation

The declaration var myVar is hoisted to the top of the scope, so the variable exists but is initialized with undefined until the assignment is made. This is why the first console.log does not throw a reference error.


Example 2: Hoisting within a Function

// Example of hoisting inside a function scope
function testHoisting() {
  console.log(message); // Outputs: undefined
  var message = "Hoisting is powerful!";
  console.log(message); // Outputs: "Hoisting is powerful!"
}
testHoisting();

Explanation

Just like in the global scope, var message is hoisted to the top of the testHoisting function's scope, making it undefined at the start of the function.


Example 3: let and const are Not Hoisted

// let and const are not hoisted in the same way
// console.log(x); // Throws ReferenceError: Cannot access 'x' before initialization
let x = 10;

Explanation

Variables declared with let and const are in a "temporal dead zone" from the start of the block until the declaration is encountered. Accessing them before they are declared results in a ReferenceError, preventing the unpredictable behavior seen with var.


Example 4: Function Declaration Hoisting

// Example of function declaration hoisting
sayHello(); // Outputs: "Hello from a hoisted function!"

function sayHello() {
  console.log("Hello from a hoisted function!");
}

Explanation

The entire function declaration sayHello is hoisted to the top of its scope. This allows you to call the function before its physical location in the code.


Example 5: Function Expressions are Not Hoisted

// Function expressions are not hoisted
// sayGoodbye(); // Throws TypeError: sayGoodbye is not a function

var sayGoodbye = function() {
  console.log("Goodbye!");
};

Explanation

When a function is assigned to a variable, it's a function expression. The variable declaration (var sayGoodbye) is hoisted, but the function body is not. At the time of the call, sayGoodbye is undefined, hence the TypeError.


Example 6: Hoisting with Mixed Declarations

// Example with both variable and function hoisting
console.log(myVariable); // Outputs: undefined
var myVariable = "I am a variable.";
myFunction(); // Outputs: "I am a function."

function myFunction() {
  console.log("I am a function.");
}

Explanation

JavaScript first hoists the var declaration, initializing myVariable to undefined. Then, it hoists the entire myFunction declaration. This allows myFunction to be called successfully while myVariable is undefined initially.


Example 7: Declaration vs. Initialization

// Hoisting only moves the declaration, not the initialization.
var a = 1;
function hoistExample() {
  console.log(a); // Outputs: undefined
  var a = 2;
  console.log(a); // Outputs: 2
}
hoistExample();
console.log(a); // Outputs: 1

Explanation

Inside hoistExample, the local var a is hoisted, shadowing the global a. Its declaration is moved to the top of the function, so the first console.log shows undefined. The global a remains unaffected.