Scope & Closures
Closures — How & Why
Master closures in JavaScript. Learn how inner functions capture and retain access to outer function variables even after execution finishes.
1. Introduction
A Closure is the combination of a function bundled together with references to its surrounding state (the lexical environment). In JavaScript, closures are created automatically every time a function is defined, giving it access to its parent scope.
2. Why It Matters
Closures are essential for advanced JavaScript patterns, such as creating private variables, implementing callbacks, and currying functions.
3. Real-World Analogy
Think of a Backpack:
- Outer Function (Preparing for a Trip): You pack items into your backpack (assign variables) and step out of your house (outer function terminates).
- Closure (The Backpack): As you travel elsewhere, you carry the backpack with you. Even though you are far from home, you can reach inside and retrieve the packed items (access parent variables) whenever you need them.
4. How It Works
When a function returns an inner function, the inner function retains a reference to the outer function's scope (its variables and parameters), even after the outer function has finished executing.
5. Internal Memory Mechanics
Normally, when a function exits, its local variables are popped off the Call Stack and garbage collected. However, if an inner function is created and references those variables, the JavaScript engine stores the outer lexical environment in the heap instead of the stack. This keeps the variables alive as long as the inner function is referenced.
6. Practical Example
This script shows how closures are used to create private variables and methods:
7. Common Mistakes
- Unintentional memory retention: Keeping references to large objects inside closures prevents them from being garbage collected, which can lead to memory leaks.
8. Quick Quiz
Q1: Where does JavaScript keep variables referenced by closures when the parent function exits?
A) Call Stack
B) Heap
Answer: B — When variables are captured by a closure, they are moved to the heap so they survive after the parent execution stack frame is popped.
9. Scenario-Based Challenge
The Private Token Cache:
An API client manages authorization: createClient(apiToken). The client should return object methods to fetch records, but the raw apiToken must not be visible on the returned client object. Write a secure closure to implement this behavior.
10. Debugging Exercise
Explain why these counters do not increment independently, and how to fix it:
let globalCount = 0; // global state
function createBadCounter() { return function() { globalCount++; return globalCount; }; } const counterA = createBadCounter(); const counterB = createBadCounter(); console.log(counterA()); // 1 console.log(counterB()); // 2 (Expected: 1)
View Solution
Diagnosis: The function references a single global variable instead of capturing local state, meaning both counter instances modify the same shared count.
Fix: Declare the counter variable inside the factory scope to create isolated closures for each instance:
function createCounter() {
let count = 0; // local scoped instance state
return function() {
count++;
return count;
};
}
11. Interview Questions
🟢 Q1: What is a closure in JavaScript, and what are its main use cases?
Answer: A closure is a function that retains access to variables from its parent lexical scope, even after the parent function has finished executing. The main use cases for closures are:
• Creating private variables and state management modules.
• Creating stateful functions, like curried helpers or custom callbacks.
• Preserving state inside event handlers and timing functions.
12. Production Considerations
- • Memory Management: Closures consume memory because they keep references to outer scope variables. Clear references to closure functions when they are no longer needed (e.g. setting the handler reference to
null) to allow the garbage collector to free the captured variables.