JavaScript Encapsulation

Encapsulation is a core principle of Object-Oriented Programming that helps protect data inside objects and only expose necessary parts. In JavaScript, encapsulation helps you control access to properties and methods, making your code more secure and easier to maintain.



What is Encapsulation?

Encapsulation means bundling data (properties) and methods that operate on the data within one unit (class), and restricting direct access to some of the object's components. This protects the integrity of the data by preventing outside code from changing it directly.

  • Private properties – Data that is hidden inside an object and can't be accessed directly from outside.
  • Public methods – Functions exposed by the object to safely interact with the private data.
  • Getters and setters – Special methods to read or update private properties in a controlled way.

Private Properties

Private properties are variables inside a class that cannot be accessed or changed directly from outside the class. They help protect the internal state of an object, so other parts of your program can’t accidentally modify important data.

In modern JavaScript (ES2020+), you create private properties by prefixing the variable name with a #. This makes the property truly private and only accessible inside the class.

Here's an example of a Person class using a private property #name:

javascript
class Person {
  #name; // Private property

  constructor(name) {
    this.#name = name; // Set private property inside constructor
  }

  greet() {
    console.log(`Hello, my name is ${this.#name}.`);
  }
}

const person = new Person("Max");
person.greet(); // Output: Hello, my name is Max.

// Trying to access the private property directly will cause an error:
console.log(person.#name); // SyntaxError: Private field '#name' must be declared in an enclosing class

How It Works:

  • The #name property is private and can only be used inside the Person class.
  • The constructor sets the private #name when creating a new object.
  • The greet() method accesses the private property to display a message.
  • Attempting to access #name directly from outside the class causes a syntax error.

🧠 Using private properties helps keep your object’s data safe from accidental changes or misuse.


Getters and Setters

Getters and setters are special methods that allow you to control how properties of an object are accessed and modified. They let you define custom behavior when reading or updating a property, while keeping the actual data private or protected.

In JavaScript classes, you create a getter using the get keyword and a setter using the set keyword.

Here’s an example of a Person class with a private property #name, and getter and setter methods for accessing and updating the name:

javascript
class Person {
  #name;

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

  // Getter for #name
  get name() {
    return this.#name;
  }

  // Setter for #name
  set name(newName) {
    if (newName.length > 0) {
      this.#name = newName;
    } else {
      console.log("Name must not be empty.");
    }
  }

  greet() {
    console.log(`Hello, my name is ${this.#name}.`);
  }
}

const person = new Person("Max");
person.greet(); // Output: Hello, my name is Max.

console.log(person.name); // Access name using getter: Max

person.name = "Bob"; // Update name using setter
person.greet(); // Output: Hello, my name is Bob.

person.name = ""; // Attempt to set an invalid name
// Output: Name must not be empty.

How It Works:

  • The private property #name stores the actual data.
  • The get name() method lets you read the private #name safely.
  • The set name(newName) method lets you update #name, but also adds validation to prevent empty names.
  • You use the getter and setter just like normal properties (person.name), making your code cleaner and safer.

🧠 Getters and setters give you control over how your object's data is accessed and modified, helping keep your code robust and easier to maintain.


Real-World Example 1: Bank Account

Let's see how encapsulation works with a real-world example: a Bank Account. We want to keep the account balance private so it can only be changed through specific methods like deposits and withdrawals. This protects the balance from being modified directly.

javascript
class BankAccount {
  #balance; // Private property

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

  // Getter to check the balance safely
  get balance() {
    return this.#balance;
  }

  // Deposit money into the account
  deposit(amount) {
    if (amount > 0) {
      this.#balance += amount;
      console.log(`Deposited $${amount}. New balance: $${this.#balance}`);
    } else {
      console.log("Deposit amount must be positive.");
    }
  }

  // Withdraw money from the account
  withdraw(amount) {
    if (amount > 0 && amount <= this.#balance) {
      this.#balance -= amount;
      console.log(`Withdrew $${amount}. New balance: $${this.#balance}`);
    } else {
      console.log("Invalid withdrawal amount.");
    }
  }
}

const myAccount = new BankAccount(100);
console.log(myAccount.balance); // Output: 100

myAccount.deposit(50);  // Deposited $50. New balance: $150
myAccount.withdraw(70); // Withdrew $70. New balance: $80

// Trying to change balance directly will not work:
// myAccount.#balance = 1000; // SyntaxError

How It Works:

  • The #balance is a private property holding the account’s money.
  • The get balance() lets us check the balance without giving direct access.
  • deposit() and withdraw() methods control how money is added or removed, with checks to prevent invalid operations.
  • This protects the account’s balance from accidental or unauthorized changes.

Output

100
Deposited $50. New balance: $150
Withdrew $70. New balance: $80

🧠 Encapsulation helps keep important data safe and forces all changes to go through controlled methods, improving reliability and security.


Real-World Example 2: User Profile

Another common use of encapsulation is in managing a User Profile. Sensitive information like the user’s password should be private and not accessible directly. Instead, methods allow safe updating and verification.

javascript
class UserProfile {
  #password; // Private property

  constructor(username, password) {
    this.username = username;
    this.#password = password;
  }

  // Method to check if a password matches the stored one
  checkPassword(input) {
    return input === this.#password;
  }

  // Method to change the password with validation
  changePassword(oldPassword, newPassword) {
    if (this.checkPassword(oldPassword)) {
      this.#password = newPassword;
      console.log("Password changed successfully.");
    } else {
      console.log("Incorrect old password. Password not changed.");
    }
  }
}

const user = new UserProfile("john_doe", "abc123");

console.log(user.username); // Output: john_doe
console.log(user.checkPassword("abc123")); // Output: true

user.changePassword("wrongpass", "newpass");  // Incorrect old password. Password not changed.
user.changePassword("abc123", "newpass");     // Password changed successfully.

console.log(user.checkPassword("newpass"));   // Output: true

// Trying to access password directly will fail:
// console.log(user.#password); // SyntaxError

How It Works:

  • The #password is private and cannot be accessed directly.
  • The checkPassword() method safely verifies a password attempt.
  • changePassword() lets the user update their password only if the old password is correct.
  • This protects sensitive data and ensures password changes are validated.

Output

john_doe
true
Incorrect old password. Password not changed.
Password changed successfully.
true

🧠 Encapsulation here helps secure user data and ensures controlled access and updates to sensitive properties.


Frequently Asked Questions

What is encapsulation in JavaScript?

Encapsulation in JavaScript is the concept of restricting access to certain properties and methods of an object, protecting its internal state and exposing only necessary parts.


How can I create private properties in JavaScript classes?

You can create private properties in JavaScript classes by using the hash (#) prefix before property names or by using closures to hide variables.


What are getters and setters used for in encapsulation?

Getters and setters allow controlled access to private properties. They let you read or modify the values while preserving encapsulation and adding validation if needed.


Why is encapsulation important in JavaScript programming?

Encapsulation helps improve code maintainability, security, and reduces complexity by hiding internal details and exposing only what is necessary.


Can encapsulation help prevent accidental modification of object properties?

Yes, encapsulation protects private properties from being modified directly, preventing bugs and unintended side effects.



What's Next?

Coming up next, you'll learn about hoisting in JavaScript. Hoisting is a concept that explains how JavaScript moves declarations (like variables and functions) to the top of the code behind the scenes. Understanding hoisting helps you avoid common mistakes and write more reliable code.