Scope determines where variables are accessible. Understanding the scope chain is essential for closures, hoisting, and debugging.
Types of Scope
Global Scope
var globalVar = "I'm global";
let globalLet = "I'm also global";
// Accessible everywhereFunction Scope
function myFunc() {
var functionScoped = "Only inside myFunc";
// var is function-scoped
}
console.log(functionScoped); // ReferenceError!Block Scope (let/const)
if (true) {
let blockScoped = "Only inside this block";
const alsoBlock = "Same here";
var notBlock = "I escape the block!"; // var ignores block scope
}
console.log(blockScoped); // ReferenceError!
console.log(notBlock); // "I escape the block!"Lexical Scoping
JavaScript uses lexical (static) scoping — scope is determined by where code is WRITTEN, not where it's called.
const x = 10;
function outer() {
const x = 20;
function inner() {
console.log(x); // 20 — looks up to where inner is DEFINED
}
inner();
}
function another() {
const x = 30;
outer(); // inner still prints 20, not 30
}The Scope Chain
const global = "global";
function outer() {
const outerVar = "outer";
function middle() {
const middleVar = "middle";
function inner() {
const innerVar = "inner";
// Scope chain: inner → middle → outer → global
console.log(innerVar); // Found in inner scope
console.log(middleVar); // Found in middle scope
console.log(outerVar); // Found in outer scope
console.log(global); // Found in global scope
}
inner();
}
middle();
}Temporal Dead Zone (TDZ)
// let and const are hoisted but NOT initialized
console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 5; // TDZ ends here
// var IS initialized (to undefined)
console.log(y); // undefined (not an error!)
var y = 5;Module Scope
// Each module has its own scope
// Variables don't leak to global
const moduleVar = "Only accessible in this module";
// Must explicitly export to share
export const shared = "Accessible to importers";Common Gotchas
- Variables declared without var/let/const become global (sloppy mode)
- Function declarations are hoisted entirely; function expressions are not
- Block-scoped variables in loops create a new binding per iteration
- The scope chain is fixed at function creation, not execution