Quick Overview
A returned function keeps the variables it was created beside, even after the outer call has finished and its frame is gone. That is what makes a private `count` possible with no object at all.
A closure is created when an inner function retains access to variables from its outer lexical scope, even AFTER the outer function has finished executing and returned!
Closures power data privacy, private variables, event handlers, factory functions, and stateful hooks in modern JavaScript frameworks like React.
When an outer function returns an inner function, the inner function carries a reference to its outer lexical scope in a persistent memory closure:
function createCounter() {
let count = 0; // Private state variable
return function() {
count = count + 1;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
Notice how count remains accessible and remembers its updated state across calls, even though createCounter() finished executing long ago!
[ Execution Context ] -> createCounter() finished & popped off Call Stack
|
v
[ Closure Scope ] -------> count = 3 (Persisted in heap memory!)
^
|
[ Returned Function ] -> counter() reads & updates 'count'
function createBankAccount(initialBalance) {
let balance = initialBalance; // Fully private variable!
return {
deposit(amount) {
balance += amount;
return balance;
},
getBalance() {
return balance;
},
};
}
const account = createBankAccount(100);
console.log(account.deposit(50)); // 150
console.log(account.balance); // undefined (Cannot access private state directly!)
var loop captures the final loop value. Use let in loops so each iteration creates a fresh closure environment!Write it yourself. The tests below run against your code.
Closures give functions durable state memory. Next, let's transition to Phase 3 and master Arrays and Objects!
Primitives copy by value and objects copy by reference, so `b = a` duplicates a number but shares an object. That single distinction explains most surprise mutations in a codebase.
`const` prevents reassignment, not mutation: a `const` object's properties can still be changed. `let` is scoped to its block, while `var` is function-scoped and leaks out of loops.
Only backticks interpolate: `${user}` reads the variable, while the identical text in quotes prints literally. Strings are immutable, so every method returns a new one rather than editing in place.