Scope determines where a variable is visible. JavaScript has three layers of it:
let globalVar = "I'm everywhere"; // global scope — visible in the whole file/module
function outer() {
let functionVar = "I'm inside outer()"; // function scope — visible anywhere inside outer, including nested functions
if (true) {
let blockVar = "I'm inside this if-block only"; // block scope — visible only within { }
}
console.log(blockVar); // ReferenceError — blockVar doesn't exist out here
}
var is function-scoped (it ignores block boundaries entirely); let and const are block-scoped (confined to the nearest enclosing { }, whether that's an if, a for loop, or a bare block). This single difference explains a huge number of surprises:
function example() {
if (true) {
var x = 1;
let y = 2;
}
console.log(x); // 1 — var ignores the block, it's scoped to the whole function
console.log(y); // ReferenceError — y is scoped to the if-block only
}
Nested scopes can read variables from any enclosing scope (this is called the scope chain) but not the reverse — an outer scope can never see a variable declared inside an inner one.