💡 Key Takeaways:
🔒 Encapsulation: Closures let functions “remember” variables from their outer scope.
🧠 Lexical Scope: Variables are stored in the scope where the function was defined, not where it’s called.
⚡ Persistent State: Closures allow functions to maintain state between calls.
🛠️ Best For: Private counters, factories, and event handlers that need remembered values.
🧠 What Is a Closure?
- A function inside another function that remembers variables from its parent
- Use for: data privacy, stateful functions, factory functions
- Returns: the inner function with access to outer variables
function outer() {
let count = 0;
return function inner() {
count++;
return count;
};
}
const counter = outer();
counter(); // 1
counter(); // 2
🔍 How It Works
- Inner functions keep a reference to the outer scope
- The outer function’s variables stay alive as long as the inner function exists
- Useful for keeping data private and persistent
function greet(name) {
return function() {
console.log(`Hello, ${name}!`);
};
}
const sayHi = greet('Ada');
sayHi(); // "Hello, Ada!"
🧩 Lexical Scope
- Closures use lexical scope, meaning variables are found where functions are defined, not called
- Use for: predictable access to outer data
- Returns: values from outer scope correctly
const city = 'Paris';
function outer() {
const city = 'London';
return function inner() {
console.log(city); // "London", not "Paris"
};
}
outer()();
🔄 Common Use Cases
1️⃣ Private Variables
- Hide internal state from outside access
- Use for: counters, configuration, security
- Returns: methods that can modify private data
function createCounter() {
let count = 0;
return {
increment: () => ++count,
get: () => count
};
}
const c = createCounter();
c.increment(); // 1
c.get(); // 1
2️⃣ Function Factories
- Create multiple similar functions sharing behavior
- Use for: preconfigured callbacks or calculators
- Returns: new functions with remembered values
function multiplier(x) {
return n => n * x;
}
const double = multiplier(2);
double(5); // 10
3️⃣ Event Handlers
- Capture variables in UI callbacks
- Use for: remembering data when events trigger later
- Returns: event function with preserved context
function setupButton(msg) {
button.addEventListener('click', () => console.log(msg));
}
setupButton('Clicked!'); // remembers msg
4️⃣ Async Callbacks
- Keep data alive across time delays
- Use for: timeouts, promises, API results
- Returns: function that can still access earlier values
function delayed(name) {
setTimeout(() => console.log(`Hi ${name}`), 1000);
}
delayed('Sam'); // still remembers "Sam"