Ways to Achieve Encapsulation in JavaScript
Encapsulation in JavaScript can be achieved in multiple ways. Each method has its own use case & benefits. Let’s discuss the most common techniques:
1. Using Closures
Closures are one of the oldest & most effective ways to achieve encapsulation in JavaScript. By defining variables inside a function & returning methods that interact with those variables, you can create a private scope.
For example:
function createBankAccount() {
let balance = 0; // Encapsulated variable
return {
deposit: function(amount) {
if (amount > 0) {
balance += amount;
console.log(`Deposited: ${amount}, New Balance: ${balance}`);
} else {
console.log("Invalid deposit amount");
}
},
withdraw: function(amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
console.log(`Withdrawn: ${amount}, New Balance: ${balance}`);
} else {
console.log("Invalid withdrawal amount");
}
},
getBalance: function() {
return balance;
}
};
}
const account = createBankAccount();
account.deposit(500); // Output: Deposited: 500, New Balance: 500
account.withdraw(200); // Output: Withdrawn: 200, New Balance: 300
console.log(account.getBalance());

You can also try this code with Online Javascript Compiler
Run Code
Output
300
In this example, the `balance` variable is encapsulated within the `createBankAccount` function. It cannot be accessed directly from outside. The only way to interact with it is through the `deposit`, ‘withdraw', & ‘getBalance’ methods.
2. Using Classes with Private Fields
With the introduction of private fields in ES2022, encapsulation in JavaScript became even more straightforward. Private fields are declared using the `` symbol & can only be accessed within the class.
For example:
class BankAccount {
balance = 0; // Private field
deposit(amount) {
if (amount > 0) {
this.balance += amount;
console.log(`Deposited: ${amount}, New Balance: ${this.balance}`);
} else {
console.log("Invalid deposit amount");
}
}
withdraw(amount) {
if (amount > 0 && amount <= this.balance) {
this.balance -= amount;
console.log(`Withdrawn: ${amount}, New Balance: ${this.balance}`);
} else {
console.log("Invalid withdrawal amount");
}
}
getBalance() {
return this.balance;
}
}
const account = new BankAccount();
account.deposit(1000); // Output: Deposited: 1000, New Balance: 1000
account.withdraw(300); // Output: Withdrawn: 300, New Balance: 700
console.log(account.getBalance());

You can also try this code with Online Javascript Compiler
Run Code
Output:
700
In this example, the `balance` variable is private & cannot be accessed directly outside the class. The methods `deposit`, `withdraw`, & `getBalance` are the only ways to interact with it.
3. Using Modules
JavaScript modules allow you to encapsulate code by splitting it into separate files. Each module has its own scope, & you can control what is exposed to other modules using `export` & `import`.
For example:
bankAccount.js
let balance = 0; // Encapsulated variable
export function deposit(amount) {
if (amount > 0) {
balance += amount;
console.log(`Deposited: ${amount}, New Balance: ${balance}`);
} else {
console.log("Invalid deposit amount");
}
}
export function withdraw(amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
console.log(`Withdrawn: ${amount}, New Balance: ${balance}`);
} else {
console.log("Invalid withdrawal amount");
}
}
export function getBalance() {
return balance;
}
main.js
import { deposit, withdraw, getBalance } from './bankAccount.js';
deposit(500); // Output: Deposited: 500, New Balance: 500
withdraw(200); // Output: Withdrawn: 200, New Balance: 300
console.log(getBalance());

You can also try this code with Online Javascript Compiler
Run Code
Output:
300
In this example, the `balance` variable is encapsulated within the `bankAccount.js` module. It cannot be accessed directly from outside the module. The only way to interact with it is through the `deposit`, `withdraw`, & `getBalance` functions.
Points to Keep in Mind for Achieving Effective Encapsulation in JavaScript
Encapsulation is a powerful concept, but it requires careful implementation to be effective. Let’s take a look at some of the important points to keep in mind when working with encapsulation in JavaScript:
1. Use Private Fields for Data Protection
Private fields (introduced in ES2022) are the most secure way to encapsulate data in JavaScript. They ensure that sensitive data cannot be accessed or modified directly from outside the class.
Example:
class User {
password; // Private field
constructor(username, password) {
this.username = username;
this.password = password;
}
validatePassword(inputPassword) {
return inputPassword === this.password;
}
}
const user = new User("john_doe", "secure123");
console.log(user.validatePassword("wrong"));
console.log(user.validatePassword("secure123"));

You can also try this code with Online Javascript Compiler
Run Code
Output:
false
true
In this example, the `password` field is private & cannot be accessed directly. The `validatePassword` method is the only way to interact with it.
2. Avoid Over-Encapsulation
While encapsulation is important, over-encapsulation can make your code unnecessarily complex. Only encapsulate data that needs to be protected or hidden.
Example:
class Product {
constructor(name, price) {
this.name = name; // No need to encapsulate
this.price = price; // No need to encapsulate
}
}
const product = new Product("Laptop", 1000);
console.log(product.name);
console.log(product.price);

You can also try this code with Online Javascript Compiler
Run Code
Output:
Laptop
1000
In this example, the `name` & `price` fields do not need to be encapsulated because they are not sensitive or prone to misuse.
3. Use Getters & Setters for Controlled Access
Getters & setters allow you to control how data is accessed & modified. They are especially useful when you need to add validation or logic before updating a value.
Example:
class Temperature {
celsius; // Private field
constructor(celsius) {
this.celsius = celsius;
}
get celsius() {
return this.celsius;
}
set celsius(value) {
if (value < -273.15) {
console.log("Temperature cannot be below absolute zero");
} else {
this.celsius = value;
}
}
}
const temp = new Temperature(25);
console.log(temp.celsius);
temp.celsius = -300;
console.log(temp.celsius);

You can also try this code with Online Javascript Compiler
Run Code
Output:
25
Temperature cannot be below absolute zero
25
In this example, the `celsius` field is encapsulated, & the `set celsius` method ensures that the temperature cannot be set below absolute zero.
4. Keep Encapsulation Consistent Across Your Codebase
Consistency is key to maintaining clean & readable code. If you decide to use encapsulation for certain data, apply the same approach throughout your codebase.
Example:
class Employee {
salary; // Private field
constructor(name, salary) {
this.name = name;
this.salary = salary;
}
getSalary() {
return this.salary;
}
}
class Manager extends Employee {
constructor(name, salary, bonus) {
super(name, salary);
this.bonus = bonus;
}
getTotalSalary() {
return this.getSalary() + this.bonus;
}
}
const manager = new Manager("Alice", 5000, 1000);
console.log(manager.getTotalSalary());

You can also try this code with Online Javascript Compiler
Run Code
Output
6000
In this example, the `salary` field is encapsulated in the `Employee` class, & the same approach is used in the `Manager` class.
5. Test Your Encapsulated Code Thoroughly
Encapsulated code can sometimes be harder to debug, so it’s important to test it thoroughly. Use unit tests to ensure that your encapsulated methods work as expected.
Example:
class Calculator {
result = 0; // Private field
add(number) {
this.result += number;
}
subtract(number) {
this.result -= number;
}
getResult() {
return this.result;
}
}
// Unit Test
const calculator = new Calculator();
calculator.add(10);
calculator.subtract(4);
console.log(calculator.getResult());

You can also try this code with Online Javascript Compiler
Run Code
Output
6

You can also try this code with Online Javascript Compiler
Run Code
In this example, the `result` field is encapsulated, & the `add`, `subtract`, & `getResult` methods are tested to ensure they work correctly.
Benefits of Encapsulation in JavaScript
1. Data Protection
Encapsulation prevents direct modification of data, reducing unintended bugs.
2. Improved Code Maintainability
Encapsulated code is easier to manage and update.
3. Better Data Control
Encapsulation allows controlled access to data through getter and setter methods.
4. Encourages Modularity
Encapsulation helps in creating modular and reusable code components.
Frequently Asked Questions
What is encapsulation in JavaScript?
Encapsulation is the practice of keeping certain parts of code private to prevent direct modification and ensure data security.
How do closures help in encapsulation?
Closures create private variables inside a function, restricting direct access to them while providing controlled interaction through methods.
What is the difference between closures and class-based encapsulation?
Closures use functions to create private variables, while classes use the # symbol to define private fields within a class structure.
Conclusion
In this article, we learned encapsulation in JavaScript, a key concept in object-oriented programming that helps in data protection and code organization. Encapsulation allows developers to restrict direct access to object properties using closures, private fields, and getter/setter methods. By applying encapsulation, we can write more secure, maintainable, and modular JavaScript code, improving overall application efficiency.