Closures: Data Hiding and Encapsulation in JavaScript
Closures let you hide data from the outside world. Here is how to use them for encapsulation and data hiding.
Closures: Data Hiding and Encapsulation in JavaScript
Data hiding is the practice of keeping internal state private, accessible only through a controlled API. Closures are the classic way to achieve this in JavaScript.
What Data Hiding Means
Internal variables should not be directly accessible or modifiable from outside. Only the methods you expose should interact with them. This prevents accidental mutation and misuse.
Basic Data Hiding With Closures
function createCounter() { let count = 0; // private return { increment: () => ++count, decrement: () => --count, getCount: () => count, }; } const counter = createCounter(); counter.increment(); counter.getCount(); // 1 counter.count; // undefined (private) `` `count` is inside `createCounter`'s lexical environment. The returned methods close over it. The outside world cannot access `count` directly. ### Encapsulation **Encapsulation** = data hiding + a controlled API. The object exposes methods that interact with the private state, but the state itself is hidden. ```js function createBankAccount(initialBalance) { let balance = initialBalance; // private return { deposit: (amount) => { if (amount <= 0) throw new Error("Invalid amount"); balance += amount; return balance; }, withdraw: (amount) => { if (amount > balance) throw new Error("Insufficient funds"); balance -= amount; return balance; }, getBalance: () => balance, }; } const acc = createBankAccount(100); acc.deposit(50); acc.getBalance(); // 150 acc.balance; // undefined (private) `` The API controls how `balance` is modified (validation, rules). Direct access is impossible. ### Comparison: Closures vs `#private` Fields ```js // Closures (classic) function createCounter() { let count = 0; return { increment: () => ++count, getCount: () => count }; } // #private fields (ES2022) class Counter { #count = 0; increment() { return ++this.#count; } getCount() { return this.#count; } } `` Both achieve data hiding. `#private` fields are on the class instance; closure variables are in the function's lexical environment. `#private` fields are more memory-efficient (methods on the prototype). Closures are more flexible (functional style, no `this`). ### Advantages of Closure-Based Data Hiding - **True privacy**: variables are completely inaccessible from outside. - **No `this` issues**: closure methods are often arrows, so `this` is not a concern. - **Functional style**: works without classes. - **Per-instance state**: each call to the factory creates a separate private scope. ### Disadvantages - **Memory**: each instance gets its own copy of the methods (not shared on a prototype). - **Harder to test**: private state is not directly inspectable. - **No inheritance**: closures do not support class-based inheritance patterns. ### The Takeaway Closures provide data hiding and encapsulation by keeping variables in a function's lexical environment and exposing only a returned API. This is the classic way to create private state in JavaScript. ES6 `#private` class fields are the modern alternative for class-based code. Use closures for functional-style privacy and `#private` for class-based privacy.
By declaring variables inside a function's lexical environment and returning methods that access them. The variables are not accessible directly from outside, only through the returned methods. This is encapsulation.
Closure variables are in the function's lexical environment (per-instance methods, functional style, no this). #private fields are on the class instance (methods shared on prototype, more memory-efficient, class-based). Both achieve true privacy.
True privacy (variables are completely inaccessible), no this issues (methods are often arrows), functional style (works without classes), and per-instance state (each factory call creates a separate private scope).
Memory (each instance gets its own copy of the methods, not shared on a prototype), harder to test (private state is not directly inspectable), and no inheritance (closures do not support class-based inheritance patterns).
Encapsulation is data hiding plus a controlled API. Closures achieve it by keeping variables private and returning methods that interact with them. The outside world can only use the returned methods, not access the state directly.
Ready to master React completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master React
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

