DEV Community

SAURAV KUMAR
SAURAV KUMAR

Posted on

Encapsulation in JavaScript — Explained in Simple Words

If you are learning Object-Oriented Programming (OOP) in JavaScript, one word that starts appearing very often is: Encapsulation

At first, it can sound like a big and confusing concept.

When I started learning it, I realized the idea is actually much simpler than the name.

Encapsulation is mainly about protecting data and controlling how that data is used.

In simple words, it helps us stop objects from being changed in the wrong way.

In this post, let’s understand encapsulation in JavaScript in the simplest way possible.


🧠 What Is Encapsulation?

Encapsulation means:

  • keeping data and methods together inside a class
  • hiding internal details from the outside world
  • allowing access through controlled methods

In simple words:

The object should control its own data.

For example, if we have a bank account, its balance should not be changed randomly from outside.

Doing this is risky:

account.balance = -5000;
Enter fullscreen mode Exit fullscreen mode

Instead, we should allow safe actions like:

account.deposit(1000);
account.withdraw(500);
Enter fullscreen mode Exit fullscreen mode

That is the core idea of encapsulation.


🔒 Why Do We Need Encapsulation?

Without encapsulation, any part of the program can directly change important values.

That can create invalid states and bugs.

For example:

  • a bank balance should not become negative randomly
  • marks should not go above 100
  • product price should not become negative
  • salary should not go below 0

Encapsulation helps us protect these values by making sure updates happen only in valid ways.

It also makes code cleaner and easier to maintain.


📦 Real-Life Analogy

Think of an ATM.

When you use an ATM, you do not open the machine and directly edit its internal data.

You only interact with the options it gives you:

  • check balance
  • deposit
  • withdraw

The internal working stays hidden.

You only use the allowed interface.

That is a simple real-world example of encapsulation.


⚙️ How Encapsulation Works in JavaScript

In JavaScript, one common way to achieve encapsulation is by using:

  • private fields
  • public methods

Private fields are written using #.

Example:

#balance
Enter fullscreen mode Exit fullscreen mode

This means that field can only be accessed inside the class.

Code outside the class cannot use it directly.


🏦 BankAccount Example

Let’s understand this with a simple example.

class BankAccount {
  #accountHolderName;
  #balance;

  constructor(accountHolderName, balance) {
    this.#accountHolderName = accountHolderName;
    this.#balance = balance > 0 ? balance : 0;
  }

  getAccountHolderName() {
    return this.#accountHolderName;
  }

  getBalance() {
    return this.#balance;
  }

  deposit(amount) {
    if (amount <= 0) {
      console.log("Deposit amount must be positive.");
      return;
    }

    this.#balance += amount;
  }

  withdraw(amount) {
    if (amount <= 0) {
      console.log("Withdrawal amount must be positive.");
      return;
    }

    if (amount > this.#balance) {
      console.log("Insufficient funds.");
      return;
    }

    this.#balance -= amount;
  }
}

const account = new BankAccount("Saurav", 5000);

console.log(account.getBalance()); // 5000
account.deposit(1000);
console.log(account.getBalance()); // 6000
account.withdraw(2000);
console.log(account.getBalance()); // 4000
Enter fullscreen mode Exit fullscreen mode

🔍 What Is Happening Here?

Let’s break it down.

1. #accountHolderName and #balance are private fields

#accountHolderName;
#balance;
Enter fullscreen mode Exit fullscreen mode

These values are hidden from outside code.

That means this is not allowed:

account.#balance = 100000; // Error
Enter fullscreen mode Exit fullscreen mode

Only the class itself can access and change #balance.


2. The constructor sets the initial values

constructor(accountHolderName, balance) {
  this.#accountHolderName = accountHolderName;
  this.#balance = balance > 0 ? balance : 0;
}
Enter fullscreen mode Exit fullscreen mode

When the object is created, the constructor initializes the private data.

We also added a small validation so invalid starting balance becomes 0.


3. getBalance() gives controlled read access

getBalance() {
  return this.#balance;
}
Enter fullscreen mode Exit fullscreen mode

If we want the balance, we ask the object for it.

We do not access it directly.


4. deposit() and withdraw() control updates

These methods decide how balance can change.

For example:

  • deposit must be positive
  • withdrawal must be positive
  • withdrawal cannot be more than current balance

This is where encapsulation becomes useful.

Instead of letting outside code change the balance however it wants, the object protects its own state.


❌ Without Encapsulation vs ✅ With Encapsulation

Without encapsulation

class BankAccount {
  constructor(name, balance) {
    this.name = name;
    this.balance = balance;
  }
}

const account = new BankAccount("Saurav", 5000);
account.balance = -10000;

console.log(account.balance); // -10000
Enter fullscreen mode Exit fullscreen mode

This is dangerous because anyone can change the balance directly.

With encapsulation

class BankAccount {
  #balance;

  constructor(balance) {
    this.#balance = balance;
  }

  getBalance() {
    return this.#balance;
  }

  withdraw(amount) {
    if (amount > 0 && amount <= this.#balance) {
      this.#balance -= amount;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Now the balance is protected.

That is much safer.


🧩 A Common Beginner Mistake

One very common mistake in JavaScript is confusing these two:

this.#balance
this.balance
Enter fullscreen mode Exit fullscreen mode

They are not the same thing.

  • this.#balance → private field
  • this.balance → normal public property

If your class uses #balance, then inside the class you must always use this.#balance.

If you accidentally write this.balance, you are working with a different property.

That can create confusing bugs.


🎯 Getters and Setters

Encapsulation is often explained using getters and setters.

Here is a simple example:

class Student {
  #marks;

  constructor(marks) {
    this.#marks = marks >= 0 && marks <= 100 ? marks : 0;
  }

  getMarks() {
    return this.#marks;
  }

  setMarks(newMarks) {
    if (newMarks >= 0 && newMarks <= 100) {
      this.#marks = newMarks;
    } else {
      console.log("Invalid marks");
    }
  }
}

const s1 = new Student(80);

console.log(s1.getMarks()); // 80
s1.setMarks(95);
console.log(s1.getMarks()); // 95
s1.setMarks(150); // Invalid marks
Enter fullscreen mode Exit fullscreen mode

Here:

  • #marks is private
  • getMarks() reads the value
  • setMarks() updates it with validation

This is a common encapsulation pattern.


🛠️ Business Methods Are Often Better Than Generic Setters

In real-world code, generic setters are not always the best choice.

For example, instead of writing this:

setBalance(amount)
Enter fullscreen mode Exit fullscreen mode

it is often better to write:

deposit(amount)
withdraw(amount)
transfer(amount)
Enter fullscreen mode Exit fullscreen mode

Why?

Because these methods reflect real actions.

They also make business rules clearer.

For a bank account, deposit() and withdraw() are more meaningful than a plain setBalance().

That is why encapsulation is not only about private variables.
It is also about controlled behavior.


✅ Benefits of Encapsulation

Here are some practical benefits:

1. Better data protection

Important values stay safe from direct misuse.

2. Better validation

You can stop invalid updates before they happen.

3. Cleaner code

The logic related to one object stays inside that object.

4. Easier maintenance

You can change internal implementation later without affecting outside code too much.

5. Better interview understanding

Encapsulation is one of the most important OOP concepts asked in interviews.


⚠️ Common Beginner Mistakes

1. Making everything public

If everything is public, outside code can change object state freely.

2. Using setters without validation

If setters accept invalid data, encapsulation becomes weak.

3. Thinking encapsulation only means private variables

Private fields are one part of it, but the bigger idea is controlled access.

4. Mixing this.balance and this.#balance

This is one of the easiest mistakes to make in JavaScript.


🎤 How to Explain Encapsulation in an Interview

If someone asks:

What is encapsulation?

You can say:

Encapsulation is the process of bundling data and methods together inside a class and restricting direct access to internal state. It helps protect data and allows controlled access through methods.

How do you achieve encapsulation in JavaScript?

You can say:

In JavaScript, encapsulation can be achieved using private fields like #balance and public methods like getters, setters, or business methods such as deposit() and withdraw().

Why is encapsulation useful?

You can say:

Encapsulation helps protect object data, prevents invalid state changes, and makes code cleaner and easier to maintain.


🚀 Final Thoughts

Encapsulation may sound like a difficult OOP term at first.

But the idea is actually simple.

It means:

  • keep data and methods together
  • hide internal details when needed
  • allow controlled access through methods

If you are learning OOP in JavaScript, start with small examples like:

  • student marks
  • product price
  • employee salary
  • bank account

Once these examples become clear, encapsulation starts feeling natural.

And after that, learning concepts like inheritance, polymorphism, and abstraction becomes much easier.


🙋‍♂️ About Me

Hi, I’m Saurav Kumar.

I enjoy learning, building, and writing about web development in simple words—especially breaking down topics that are useful for beginners and developers preparing for interviews.

Right now, I’m focusing on deepening my understanding of core concepts like JavaScript, Object-Oriented Programming, system design, and software engineering fundamentals.

Let's connect!

Top comments (0)