Scope and Closures
Scope determines where variables are accessible. Closures allow functions to remember variables from their outer scope even after that scope has finished executing.
Types of Scope
Global Scope
Variables declared outside any function or block:
let globalVar = 'I am global';
function showGlobal() {
console.log(globalVar); // accessible
}
Function Scope
Variables declared with var inside a function:
function example() {
var fnScoped = 'inside function';
console.log(fnScoped);
}
// console.log(fnScoped); // ReferenceError
Block Scope
Variables declared with let or const inside {}:
if (true) {
let blockVar = 'block';
const alsoBlock = 'block';
}
// console.log(blockVar); // ReferenceError
Lexical Scope
JavaScript uses lexical (static) scope — a function’s scope is determined by where it is written in the source code, not where it is called.
let outer = 'outer';
function outerFn() {
let inner = 'inner';
function innerFn() {
console.log(outer, inner); // both accessible
}
innerFn();
}
Closures
A closure is created when a function retains access to variables from its outer lexical environment.
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
Each call to createCounter() creates a new independent closure with its own count.
Practical Closure Uses
Private variables (module pattern)
function createBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit(amount) {
balance += amount;
return balance;
},
withdraw(amount) {
if (amount <= balance) {
balance -= amount;
}
return balance;
},
getBalance() {
return balance;
}
};
}
const account = createBankAccount(100);
account.deposit(50); // 150
account.withdraw(30); // 120
// account.balance is not accessible — private
Factory functions
function multiply(factor) {
return function(number) {
return number * factor;
};
}
const double = multiply(2);
const triple = multiply(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
Closures in Loops (Common Pitfall)
// Problem: var is function-scoped, all callbacks share same i
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 3, 3, 3
}
// Fix 1: use let (block scope)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 0, 1, 2
}
// Fix 2: IIFE
for (var i = 0; i < 3; i++) {
((j) => setTimeout(() => console.log(j), 100))(i);
}
Scope Chain
When a variable is referenced, JavaScript looks up the scope chain: current scope → outer scope → … → global scope.
let a = 1;
function outer() {
let b = 2;
function inner() {
let c = 3;
console.log(a, b, c); // 1, 2, 3
}
inner();
}
Understanding scope and closures is essential for advanced JavaScript patterns, callbacks, and asynchronous code.