🧠 JavaScript Scope & Closures Explained — With Real Examples
Master JavaScript scope and closures. Understand lexical scope, the scope chain, and how closures work — with interview questions every senior developer should nail.
Scope = where a variable is visible. Closure = a function that remembers variables from where it was defined, even after that outer function has finished running.
📏 Three kinds of scope
- Global — visible everywhere
- Function — visible only inside a function
- Block — visible inside
{ }(forlet/constonly)
🧠 The closure recipe
- A function defined inside another function
- The inner function references variables from the outer one
- The outer function returns (or passes out) the inner one
- The inner one keeps those variables alive — forever
🔥 Real-world analogy
A closure is a backpack. When a function is created, it packs all the variables it can see from its surroundings. Even when it travels far away (returned, stored, called later), it still has its backpack.
💡 Why closures are useful
- Private state (no class needed)
- Function factories
- Event handlers that remember context
- Memoization, currying, partial application
💻 Code Examples
The classic counter
function makeCounter() {
let count = 0;
return () => ++count;
}
const c = makeCounter();
c(); // 1
c(); // 2
c(); // 3Output:
Each call remembers and updates `count` — it's private to that counter instance.
Function factory
function multiplyBy(n) {
return x => x * n;
}
const double = multiplyBy(2);
const triple = multiplyBy(3);
double(5); // 10
triple(5); // 15Output:
Each returned function closes over its own `n`.
The classic var-in-loop bug
// ❌ Bug — all log 3
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// ✅ Fix — let creates a new binding per iteration
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}Output:
var: 3, 3, 3 — let: 0, 1, 2
⚠️ Common Mistakes
- Thinking closures need to 'capture' a snapshot — they reference the live variable, not its value at creation time.
- Creating closures in loops with `var` and being surprised by shared state.
- Holding huge objects in closure scope and causing memory leaks.
🎯 Interview Questions
Real questions asked at top product and service-based companies.
Q1.What is a closure in JavaScript?Beginner
A closure is a function bundled with references to its surrounding (lexical) scope. Even when the outer function returns, the inner function retains access to the outer variables.
Q2.Why does this loop log 3, 3, 3?Intermediate
Because `var` is function-scoped — all three callbacks share the same `i`. By the time setTimeout fires, the loop has finished and i === 3. Using `let` gives each iteration its own `i` binding.
Q3.What is lexical scope?Intermediate
Scope determined by where code is written, not how it's called. JavaScript uses lexical scoping — a function looks up variables in the chain of scopes it was defined in, not where it was invoked from.
Q4.How can closures cause memory leaks?Advanced
A closure keeps its enclosing scope alive. If you store closures (e.g. event handlers, intervals) that reference large objects, those objects can't be garbage collected until the closure is released.
Q5.Write a function `once(fn)` that runs fn only the first time.Advanced
function once(fn) {
let called = false, result;
return (...args) => {
if (!called) { called = true; result = fn(...args); }
return result;
};
} — uses closure to keep `called` private.
🧠 Quick Summary
- Scope = where a variable is visible.
- Closure = function + the scope it was born in.
- Closures power private state, factories and currying.
- Lexical scope: scope is based on definition, not call site.
- Beware var-in-loop and large objects in closures.