var, let, and const in JavaScript: What Actually Changed

ES6 (2015) added let and const specifically to fix problems with var‘s scoping — understanding exactly what changed clarifies a lot of confusing older JavaScript code you’ll still encounter.

// var is function-scoped, and "hoists" - accessible before its declaration line
console.log(x); // undefined, not an error
var x = 5;

// let and const are block-scoped - confined to the nearest { }
if (true) {
    let y = 10;
    const z = 20;
}
// console.log(y); // ReferenceError - y doesn't exist out here

// The classic var loop bug
for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100); // prints 3, 3, 3 - all share ONE var
}
for (let j = 0; j < 3; j++) {
    setTimeout(() => console.log(j), 100); // prints 0, 1, 2 - each iteration gets its own let
}

The classic loop-closure bug above is exactly why modern JavaScript defaults to let and const over var — let‘s per-iteration binding fixes a footgun that used to require an IIFE workaround to solve correctly.