Scope
Scope is the region of code where a variable is accessible. JavaScript uses lexical (static) scoping: a variable's scope is determined by where it's declared in the source code, not where it's called from.
Explanation
JavaScript has three scope levels: global (variables declared outside any function — accessible everywhere, attached to window in browsers), function (variables declared with var or function parameters — accessible only within that function), and block (variables declared with let/const inside {} braces — accessible only within that block). var ignores block scope; let/const respect it. The scope chain: when you reference a variable, JavaScript looks in the current scope first, then walks up through enclosing scopes to the global scope. If it's not found anywhere, it throws a ReferenceError. This chain is built at function definition time (lexical scoping) — it doesn't matter where the function is called from, only where it was defined. Closures are the direct consequence of lexical scoping: an inner function defined inside an outer function has access to the outer function's scope via the scope chain. When the outer function returns, the inner function retains its scope chain reference, keeping the outer scope alive. Variable shadowing: a variable in an inner scope with the same name as an outer scope variable hides the outer one within that inner scope. This is legal but a common source of confusion — changing the inner variable doesn't affect the outer one.
Code Example
javascript// Scope levels in JavaScript
// Global scope
const globalVar = 'global';
function outer() {
// Function scope
const functionVar = 'function';
let blockScoped;
if (true) {
// Block scope
let blockVar = 'block'; // only accessible here
blockScoped = blockVar; // assignment to outer let
var functionScoped = 'x'; // var ignores block! hoisted to function scope
}
// console.log(blockVar); // ReferenceError
console.log(functionScoped); // 'x' — var ignores block boundaries
console.log(blockScoped); // 'block'
function inner() {
// Closure: accesses outer's scope via scope chain
console.log(functionVar); // 'function'
console.log(globalVar); // 'global'
}
inner();
}
// Shadowing: inner variable hides outer
const name = 'Alice';
function greet() {
const name = 'Bob'; // shadows outer 'name' within this function
return `Hello, ${name}`; // 'Bob', not 'Alice'
}
// No block scope with var — a common bug source
for (var i = 0; i < 3; i++) {}
console.log(i); // 3 — var leaks outside the for loop!
for (let j = 0; j < 3; j++) {}
// console.log(j); // ReferenceError — let is block-scoped
Why It Matters for Engineers
Scope bugs are some of the most confusing in JavaScript. Using a variable before it's declared in the same scope, accidentally reading a shadowed variable, or relying on var's function scoping in a for loop — these are real, frequent bugs. Understanding scope makes you immune to an entire category of JavaScript gotchas. Scope also explains closures (how inner functions access outer variables), module scope (ES modules have their own scope, preventing global pollution), and why React's hooks use closures to capture state values — which leads to stale closure bugs when dependencies change.
Learn This In Practice
Go deeper with the full module on Beyond Vibe Code.
Programming Foundations → →