JavaScript Notes
Master JavaScript closures with deep conceptual explanations, memory diagrams, practical examples, interview questions, and real-world use cases. IIFE, memoize, debounce, module pattern.
What is a Closure?
A closure is a function that remembers and can access variables from its outer (enclosing) scope even after the outer function has finished executing. In other words, a closure gives a function access to its lexical environment permanently.
Every function in JavaScript forms a closure at creation time. The closure is the combination of a function and the lexical environment within which that function was declared.
Why Closures Matter
Closures are one of the most powerful and frequently misunderstood concepts in JavaScript. They enable:
- Data privacy and encapsulation
- State preservation across function calls
- Module pattern implementation
- Callback functions and event handlers
- Functional programming patterns like currying and memoization
- Factory functions for creating specialized functions
How Closures Work Internally
The Execution Context Model
When JavaScript code runs, it creates execution contexts. Each context has:
- Variable Environment - stores local variables and function declarations
- Scope Chain - reference to outer environments
- this binding - context reference
+---------------------------------------------+
| Global Execution Context |
| Variables: globalVar = "hello" |
| Functions: outerFunction |
+---------------------------------------------+
| outerFunction Execution Context |
| Variables: outerVar = 10 |
| Functions: innerFunction |
| Scope Chain: -> Global |
+---------------------------------------------+
| innerFunction Execution Context |
| Variables: innerVar = 20 |
| Scope Chain: -> outerFunction -> Global|
| CLOSURE: { outerVar: 10 } |
+---------------------------------------------+Memory Diagram
When outerFunction finishes execution, its execution context is popped from the call stack. However, the variables it created are NOT garbage collected because the inner function still holds a reference to them through its [[Scope]] internal property.
Basic Closure Examples
Example 1: Simple Closure
function outerFunction() {
let outerVariable = "I am from outer function";
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
const closureFunc = outerFunction();
closureFunc();I am from outer function
Even though outerFunction has finished executing, innerFunction still has access to outerVariable because of the closure.
Example 2: Closure with Counter
Count: 1 Count: 2 Count: 3 Count: 2 2
The count variable is private - it cannot be accessed directly from outside. Only the returned methods can modify it. This is data encapsulation through closures.
Example 3: Closure Preserving State
function greeting(salutation) {
return function(name) {
console.log(`${salutation}, ${name}!`);
};
}
const sayHello = greeting("Hello");
const sayHi = greeting("Hi");
const sayNamaste = greeting("Namaste");
sayHello("Alice");
sayHi("Bob");
sayNamaste("Charlie");Hello, Alice! Hi, Bob! Namaste, Charlie!
The Classic Loop Problem
The Problem
Button 6 clicked Button 6 clicked Button 6 clicked Button 6 clicked Button 6 clicked
The var keyword does not create block scope. All callbacks share the same i variable, which is 6 after the loop ends.
Solution 1: Using IIFE
Button 1 clicked Button 2 clicked Button 3 clicked Button 4 clicked Button 5 clicked
Solution 2: Using let
Button 1 clicked Button 2 clicked Button 3 clicked Button 4 clicked Button 5 clicked
Solution 3: Using Closure Factory
Practical Use Cases of Closures
1. Module Pattern
Deposited: $1000. Balance: $1000 Deposited: $500. Balance: $1500 Withdrawn: $200. Balance: $1300 Balance: 1300
2. Function Factory
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
const quadruple = createMultiplier(4);
console.log(double(5));
console.log(triple(5));
console.log(quadruple(5));10 15 20
3. Memoization
120
120
{ hits: 1, misses: 1, size: 1 }4. Currying
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return function(...args2) {
return curried.apply(this, args.concat(args2));
};
};
}
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3));
console.log(curriedAdd(1, 2)(3));
console.log(curriedAdd(1, 2, 3));6 6 6
5. Private Methods
function createPerson(name, age) {
let _name = name;
let _age = age;
const _id = Math.random().toString(36).substr(2, 9);
function validateAge(newAge) {
return typeof newAge === "number" && newAge >= 0 && newAge <= 150;
}
return {
getName() { return _name; },
setName(newName) {
if (typeof newName === "string" && newName.trim().length > 0) {
_name = newName.trim();
return true;
}
return false;
},
getAge() { return _age; },
setAge(newAge) {
if (validateAge(newAge)) { _age = newAge; return true; }
return false;
},
getId() { return _id; },
toString() { return `Person { name: ${_name}, age: ${_age} }`; }
};
}
const person = createPerson("Rahul", 25);
console.log(person.getName());
console.log(person.getAge());
person.setName("Rahul Kumar");
console.log(person.toString());
console.log(person._name);Rahul
25
Person { name: Rahul Kumar, age: 25 }
undefined6. Once Function
function once(fn) {
let called = false;
let result;
return function(...args) {
if (!called) {
called = true;
result = fn.apply(this, args);
}
return result;
};
}
const initialize = once(function() {
console.log("Initialized!");
return { status: "ready" };
});
console.log(initialize());
console.log(initialize());Initialized!
{ status: "ready" }
{ status: "ready" }7. Debounce
8. Throttle
9. Event Handler Factory
10. API Rate Limiter
Request to /api/data allowed. Request to /api/users allowed. Request to /api/posts allowed. Rate limited. Try again later.
Memory Management with Closures
WITHOUT CLOSURE:
Function executes -> Variables created -> Function ends -> GC collects variables
WITH CLOSURE:
Function executes -> Variables created -> Function ends -> Variables KEPT (referenced by inner fn)Memory Leak Prevention
// BAD: Captures entire large array
function bad() {
const huge = new Array(1000000).fill("x");
return function() { console.log("done"); };
}
// GOOD: Only capture what is needed
function good() {
const huge = new Array(1000000).fill("x");
const len = huge.length;
return function() { console.log("Length:", len); };
}Common Mistakes
Mistake 1: Closure Over Reference
[INFO] Start [ERROR] Oops
Fix: capture primitive values instead of object references.
Mistake 2: this in Closures
Common Traps 🪤
| Trap | Problem | Fix |
|---|---|---|
var in loops | All closures share same variable | Use let or IIFE |
| Closure over object | Mutating object affects all closures | Capture primitives |
this in closure | Lost context in regular function | Use arrow function |
| Memory leak | Large objects kept alive | Capture only needed values |
| Circular references | Objects referencing each other | Nullify refs when done |
Interview Questions
Q1: Output?
Answer: 1 2 1 3 — separate closures with separate state.
Q2: Output?
let x = 10;
function foo() { console.log(x); }
function bar() { let x = 20; foo(); }
bar();Answer: 10 — lexical scope, not dynamic scope.
Q3: Output?
Answer: 3 3 3 — all share same var i.
Q4: Implement private counter with reset
Q5: What is the output?
Answer: 2 — let creates block scope per iteration.
Performance Tips
| Scenario | Advice |
|---|---|
| Small private state | Use closures freely |
| Large captured data | Extract only needed values |
| Event listeners | Remove when element is removed |
| Tight loops | Avoid creating closures inside loops |
| Long-lived closures | Nullify unused references |
Summary
Closures combine a function with its lexical scope. They enable private state, factories, memoization, debouncing, throttling, and module patterns. Understanding closures is essential for JavaScript mastery and technical interviews.
Practice Exercises
- Implement
makeAccumulator()that keeps a running total - Build
retry(fn, n)that retries a function up to n times - Create a pub/sub system using closures
- Implement
partial(fn, ...args)for partial application - Build a simple state machine using closures
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Closures in JavaScript — Complete Guide with Examples 2026.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this JavaScript Master Course topic.
Search Terms
javascript-complete-guide, javascript master course, javascript, complete, guide, advanced, closures, closures in javascript — complete guide with examples 2026
Related JavaScript Master Course Topics