JavaScript Scope and Hoisting Explained Clearly

Hoisting is JavaScript’s behavior of processing variable and function declarations before executing code line by line — understanding it explains a lot of otherwise-confusing behavior around when things become available.

// Function declarations are fully hoisted - usable before their definition
sayHello(); // works fine
function sayHello() { console.log('Hello'); }

// var is hoisted but only the declaration, not the assignment
console.log(x); // undefined, not an error
var x = 5;

// let/const are hoisted too, but land in the "temporal dead zone"
// until their actual declaration line - accessing them early throws
try {
    console.log(y); // ReferenceError: Cannot access 'y' before initialization
    let y = 10;
} catch (e) { console.log(e.message); }

// Function expressions are NOT hoisted the same way
try {
    sayGoodbye(); // TypeError: sayGoodbye is not a function
    var sayGoodbye = function() { console.log('Goodbye'); };
} catch (e) { console.log(e.message); }

The practical takeaway: declare variables at the top of their scope and functions before use, regardless of hoisting rules — relying on hoisting behavior to make code work is a maintenance liability even when it’s technically valid.