Overview & Purpose
Scope determines WHERE in your code a variable is accessible. It answers the question: "Can I use this variable here?"
JavaScript has three types of scope:
| Scope | Created by | Accessible |
|-------|------------|-----------|
| **Global** | Outside all functions/blocks | Everywhere in the file |
| **Function** | Inside a `function` body | Only inside that function |
| **Block** | Inside `{ }` with `let`/`const` | Only inside that block |
Why scope matters:
Variables in different scopes can have the same name without conflict
Scope protects variables from being accidentally modified by unrelated code
Understanding scope is REQUIRED for debugging "variable is not defined" errors
Scope is the foundation of closures, modules, and encapsulation
Topic Definition
Scope is the region of code where a variable is defined and accessible.
Think of it like buildings:
Global scope = the street (everyone can access)
Function scope = a private office (only people inside)
Block scope = a locked cabinet inside the office
Global scope
├── globalVar = "I'm everywhere"
│
├── function outer() {
│ ├── outerVar = "I'm in outer only"
│ │
│ └── function inner() {
│ ├── innerVar = "I'm in inner only"
│ └── Can access: innerVar, outerVar, globalVar ✅
│ }
│ Can access: outerVar, globalVar ✅
│ Cannot access: innerVar ❌
│}
│
Can access: globalVar ✅
Cannot access: outerVar, innerVar ❌This layered access is called the scope chain — inner scopes can access outer scopes, but not vice versa.
Why It Matters
Understanding scope prevents three categories of bugs:
1. "Variable is not defined" errors — you're trying to use a variable outside its scope
2. Variable collision — two different var declarations overwrite each other
3. Unexpected global variables — forgetting const/let creates a global variable
Real consequence example:
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3 (NOT 0, 1, 2!)
// Because var is function-scoped — all timeouts share the SAME i
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2 ✅
// Because let is block-scoped — each iteration has its OWN iThis is one of the most classic JavaScript interview questions!
Syntax Guide
// ═══════════════════════════════════════════════
// 1. GLOBAL SCOPE
// ═══════════════════════════════════════════════
const appName = "MyApp"; // Global — accessible everywhere
let currentUser = null; // Global — changes throughout session
// ═══════════════════════════════════════════════
// 2. FUNCTION SCOPE
// ═══════════════════════════════════════════════
function processOrder(order) {
const discount = 0.10; // Function-scoped — only here
const total = order.price * (1 - discount);
return total;
// discount and total DON'T exist outside this function
}
// ═══════════════════════════════════════════════
// 3. BLOCK SCOPE (let/const)
// ═══════════════════════════════════════════════
if (true) {
const blockVar = "I'm block-scoped";
let counter = 0;
console.log(blockVar); // ✅ accessible inside block
}
// console.log(blockVar); // ❌ ReferenceError outside block
// ═══════════════════════════════════════════════
// 4. SCOPE CHAIN (inner accesses outer)
// ═══════════════════════════════════════════════
const taxRate = 0.18; // Outer scope
function calculatePrice(base) {
const withTax = base * (1 + taxRate); // ✅ accesses outer taxRate
function formatPrice() {
return "₹" + withTax.toFixed(2); // ✅ accesses parent's withTax
}
return formatPrice();
}
console.log(calculatePrice(1000));- Global scope — variables declared outside all functions, accessible everywhere
- Function scope — variables declared with var inside a function, accessible only in that function
- Block scope — variables declared with let/const inside {}, accessible only in that block
- Scope chain — JavaScript looks up the chain of parent scopes to find variables
- Lexical scope — scope is determined at write-time by code structure, not runtime
Scope is a concept, not a function. Variables either are or aren't accessible based on where they're declared.
The Scope Chain in detail:
When JavaScript encounters a variable, it searches in this order:
1. Current scope (local)
2. Parent scope
3. Grandparent scope
4. ...continue up
5. Global scope
6. If not found → ReferenceError
Global: taxRate = 0.18
└─ outer(): base = 1000
├─ reads taxRate from global ✅
└─ inner():
├─ reads base from outer ✅
└─ reads taxRate from global ✅Variable Shadowing:
When an inner scope declares a variable with the same name as an outer scope, the inner one "shadows" the outer:
const name = "Global Asha";
function greet() {
const name = "Local Ravi"; // shadows the global name
console.log(name); // "Local Ravi" — local wins
}
greet();
console.log(name); // "Global Asha" — global unchangedRunnable Code Examples
Example 1: Global, Function, and Block Scope Side by Side
See exactly where variables are and aren't accessible.
const globalMessage = "I'm global"; // Global scope
function outerFunction() {
const outerVar = "I'm in outer"; // Function scope
if (true) {
const blockVar = "I'm in block"; // Block scope
let anotherBlock = "also block";
// ✅ Can access all three scopes
console.log("Inside block:", globalMessage);
console.log("Inside block:", outerVar);
console.log("Inside block:", blockVar);
}
// ✅ Can access global and function scope
console.log("Inside outer:", globalMessage);
console.log("Inside outer:", outerVar);
// ❌ Cannot access block scope
// console.log(blockVar); // ReferenceError!
}
// ✅ Can access only global
console.log("Global level:", globalMessage);
// ❌ Cannot access function or block scope
// console.log(outerVar); // ReferenceError!
outerFunction();Inner scopes can always reach up to outer/global scopes. Outer scopes cannot access inner scopes. This is called lexical scoping — determined by where the code is written.
Example 2: The Classic var vs let Loop Problem
One of the most asked JavaScript interview questions — why does this happen?
// ❌ var: all callbacks share the SAME i
console.log("Using var:");
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log("var i =", i);
}, 100 * i);
}
// ✅ let: each iteration has its OWN i
console.log("Using let (after 500ms delay):");
for (let j = 0; j < 3; j++) {
setTimeout(function() {
console.log("let j =", j);
}, 500 + 100 * j);
}var is function-scoped — all 3 setTimeout callbacks share the SAME i variable. By the time they run (after 100ms+), the loop is done and i = 3. let is block-scoped — each iteration creates a NEW i variable, capturing the value at that moment.
Example 3: Scope Chain — How JavaScript Finds Variables
Watch JavaScript climb the scope chain to find a variable.
// Level 1: Global scope
const currency = "₹";
function showPricing() {
// Level 2: Function scope
const taxRate = 0.18;
function formatPrice(amount) {
// Level 3: Inner function scope
const withTax = amount * (1 + taxRate);
// Accesses: amount (own), taxRate (parent), currency (grandparent/global)
return currency + withTax.toFixed(2);
}
function applyDiscount(price, pct) {
// Also Level 2 — can access taxRate (sibling scope? No — parent scope)
const discounted = price * (1 - pct);
const formatted = formatPrice(discounted); // Call sibling function
return formatted;
}
console.log("Full price:", formatPrice(1000));
console.log("After 10% discount:", applyDiscount(1000, 0.10));
}
showPricing();formatPrice() can access taxRate from showPricing() (parent) and currency from global scope (grandparent). This chain — local → parent → grandparent → global — is the scope chain. JavaScript searches up until it finds the variable or throws ReferenceError.
Real-world Use Cases
- 1
Module pattern: Using function scope to create private variables that external code can't accidentally modify — the basis of encapsulation.
- 2
Event handler bugs: Variables declared with
varin loops that are shared across all event handlers — a common bug fixed by usinglet. - 3
React hooks:
useStatevalues are scoped to the component function. Understanding scope helps you know why state updates work the way they do. - 4
Avoiding global pollution: Wrapping code in functions or modules (IIFE) prevents adding to the global scope, preventing conflicts with third-party scripts.
- 5
Configuration objects: Global constants like
const API_BASE_URLare deliberately global; component-level data should be locally scoped. - 6
Debugging 'undefined' errors: 90% of 'variable is not defined' errors are scope issues — the variable exists in a different scope.
Coding Exercises
Exercise Challenge
Predict the output before running: var x = 1; function fn() { var x = 2; console.log(x); } fn(); console.log(x);
Exercise Challenge
Fix the classic loop closure bug to log 0, 1, 2 instead of 3, 3, 3.
Practice Tasks Checklist
JavaScript Scope Quiz Challenges
Quiz Challenge
Can an inner function access variables from its outer function?
Quiz Challenge
What type of scope does a variable declared with var inside an if block have?
Quiz Challenge
What is variable shadowing?
Technical Interview Q&As
1What is scope in JavaScript?
Model Answer:
Scope is the region of code where a variable is accessible. JavaScript has three scope levels: Global (accessible everywhere), Function (accessible inside the function), and Block (accessible inside {} with let/const). Inner scopes can access outer/global scopes (scope chain), but not vice versa.2What is the difference between var, let, and const regarding scope?
Model Answer:
var is function-scoped — it exists throughout the entire function regardless of which block it's declared in. let and const are block-scoped — they only exist within the {} they're declared in. This makes var unpredictable in loops and if blocks, while let/const behave as you'd expect.3What is the scope chain?
Model Answer:
When JavaScript looks up a variable, it first checks the current scope. If not found, it checks the parent scope, then grandparent, all the way to global scope. If not found anywhere, it throws ReferenceError. This lookup path is the scope chain. Inner functions can always access variables from their outer functions — this is the basis of closures.4Why does `for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0) }` log 3, 3, 3?
Model Answer:
var is function-scoped. All three setTimeout callbacks share the SAME i variable. The loop completes (i becomes 3) before any timeout runs. When they do run, they all read i which is now 3. Fix: use let instead of var. let is block-scoped — each loop iteration creates a NEW i, capturing the value at that moment. Result: 0, 1, 2.