Skip to main content

Scope & Closures

What is Scope?

Scope determines where variables are accessible in your code.

Types of Scope

ScopeDescriptionKeywords
GlobalAccessible everywherevar (outside function)
FunctionAccessible inside the function onlyvar
BlockAccessible inside {} onlylet, const
var a = "global";     // global scope

function example() {
var b = "function"; // function scope

if (true) {
let c = "block"; // block scope
var d = "leaked"; // function scope (var ignores block)
}

console.log(d); // "leaked" — var is NOT block scoped
console.log(c); // ReferenceError — let IS block scoped
}

Scope Chain

When a variable is used, JS looks for it in the current scope first, then moves up the chain:

Inner Scope → Outer Scope → ... → Global Scope
let x = "global";

function outer() {
let y = "outer";

function inner() {
let z = "inner";
console.log(x); // "global" — found via scope chain
console.log(y); // "outer" — found in outer scope
console.log(z); // "inner" — found in current scope
}
inner();
}

Closures

A closure is a function that remembers variables from its outer scope even after the outer function has returned.

function createCounter() {
let count = 0; // private variable

return {
increment: () => ++count,
getCount: () => count,
};
}

const counter = createCounter();
counter.increment();
counter.increment();
console.log(counter.getCount()); // 2
// 'count' is still alive because of closure

Classic Loop Problem

// Problem: var is function-scoped
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3 — all reference the same 'i'

// Fix 1: use let (block-scoped)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2

// Fix 2: IIFE creates a new scope
for (var i = 0; i < 3; i++) {
((j) => {
setTimeout(() => console.log(j), 100);
})(i);
}
// Output: 0, 1, 2

Practical Uses of Closures

  • Data privacy — encapsulate state.
  • Function factories — create specialized functions.
  • Memoization — cache results.
  • Event handlers — maintain state between events.
// Memoization
function memoize(fn) {
const cache = {};
return function (...args) {
const key = JSON.stringify(args);
if (cache[key]) return cache[key];
return (cache[key] = fn(...args));
};
}

Key Takeaways

  • var = function scope, let/const = block scope.
  • JS uses a scope chain to resolve variables.
  • Closures let inner functions access outer variables even after the outer function returns.
  • Closures are the foundation for data privacy and many JS patterns.