Closures for Encapsulation
Using a closure deliberately to create private state — a factory function returning an object whose internal variables can't be reached from outside.
Purpose
A closure — a function bundled with a live reference to its defining scope — can be used deliberately to create private state: variables genuinely unreachable from outside a factory function, not merely hidden by naming convention.
Example
function createBankAccount(initialBalance) {
let balance = initialBalance; // private — unreachable from outside
return {
deposit(amount) {
balance += amount;
return balance;
},
getBalance() {
return balance;
},
};
}
const account = createBankAccount(100);
account.deposit(50);
console.log(account.getBalance()); // 150
console.log(account.balance); // undefined — no property by that name exists
Behavior notes
- Each call to the factory function creates a fresh, independent closure —
createBankAccount(100)called twice produces two accounts with entirely separatebalancevariables. - This predates, and still coexists with, private class fields (
#balance, covered in Objects, Prototypes, Classes, and this) as a way to achieve the same genuine privacy without a class. - The classic loop-variable-capture bug (
varinside aforloop, all callbacks sharing one final value) is the same closure mechanism causing an unintended effect — see Execution Model, Scope, and Closures.
Common mistakes
- Believing a closure “copies” the captured variable at the moment the closure is created — it doesn’t; it holds a live reference, so a later mutation of the captured variable (from code that still has access to it) is visible through the closure too.
- Creating closures inside a hot loop or frequently-called function without considering that each one keeps its captured scope alive for as long as the closure itself is reachable.
Runtime notes
A coding pattern built on core ECMAScript closures, behaving identically anywhere JavaScript runs.