Lesson 3
Encapsulation and Privacy in JavaScript OOP
Lesson Overview

Hello! In this lesson, we're revisiting Encapsulation, Private Attributes, and Private Methods in Object-Oriented Programming (OOP). Imagine encapsulation as an invisible fence safeguarding a garden from outside interference, keeping data and methods safe within. Within this garden, certain plants (Private Attributes and Methods) are only for the gardener's eyes. These are crucial for making your classes more robust and secure!

Encapsulation Explained

Encapsulation in OOP wraps up data and methods into a class. This organizational approach tidies the code and reinforces security. If you were to code a multiplayer game, for example, you could create a Player class, encapsulating data (health, armor, stamina) and methods (receiveDamage, shieldHit, restoreHealth).

JavaScript
1class Player { 2 constructor(health, armor, stamina) { 3 this.health = health; 4 this.armor = armor; 5 this.stamina = stamina; 6 } 7 8 receiveDamage(damage) { 9 this.health -= damage; // Reduce health 10 } 11 12 shieldHit(armor) { 13 this.armor -= armor; // Decrease armor 14 } 15 16 restoreHealth(healthIncrease) { 17 this.health += healthIncrease; // Restore health 18 } 19} 20 21const player = new Player(100, 50, 77);

Now, player is an instance of the Player class on which you can call methods.

Remark the Privacy

In JavaScript, a # before the attribute or method name designates it as private. Note that the constructor itself cannot be private.

JavaScript
1class PrivateExample { 2 #privateAttribute; // Declare a private field 3 4 constructor() { 5 this.publicAttribute = "Public"; 6 this.#privateAttribute = "Private"; // Initialize private attribute 7 } 8 9 getPrivateAttribute() { 10 return this.#privateAttribute; 11 } 12} 13 14const example = new PrivateExample(); 15console.log(example.publicAttribute); // Works: logs "Public" 16console.log(example.getPrivateAttribute()); // Works: logs "Private" 17console.log(example.#privateAttribute); // Error: can't access private attribute from outside

Private attributes and methods are inaccessible directly from an instance. This arrangement helps maintain integrity.

Private Attributes

Private Attributes, which can only be altered via class methods, limit outside interference. For instance, a BankAccount class might feature a #balance private attribute that one could change only through deposits or withdrawals.

JavaScript
1class BankAccount { 2 #balance; // Declare a private field 3 4 constructor(accountNumber, balance) { 5 this.accountNumber = accountNumber; 6 this.#balance = balance; // Initialize the private attribute 7 } 8 9 deposit(amount) { 10 this.#balance += amount; // Deposit money 11 } 12 13 getBalance() { 14 return this.#balance; // A public method to access balance 15 } 16} 17 18const bankAccount = new BankAccount(1234, 100); 19console.log(bankAccount.getBalance()); // Works: logs 100 20console.log(bankAccount.#balance); // Error: can't access private attribute from outside

Here, #balance is private, thus ensuring the integrity of the account balance. It can't be accessed directly from outside the class.

Private Methods

Like private attributes, private methods are accessible only within their class. Here's an example:

JavaScript
1class BankAccount { 2 #balance; // Declare a private field 3 4 constructor(accountNumber, balance) { 5 this.accountNumber = accountNumber; 6 this.#balance = balance; 7 } 8 9 // Private method 10 #addInterest(interestRate) { 11 this.#balance += this.#balance * interestRate; // Calculation of interest 12 } 13 14 // Public method calling the private method 15 addYearlyInterest() { 16 this.#addInterest(0.02); // Adds 2% interest 17 } 18 19 getBalance() { 20 return this.#balance; 21 } 22} 23 24const bankAccount = new BankAccount(1234, 100); 25bankAccount.addYearlyInterest(); // Works, calling a public method 26console.log(bankAccount.getBalance()); // Logs the updated balance 27bankAccount.#addInterest(0.1); // Error: can't call a private method from outside

Here, addYearlyInterest is a public method that calls the private method #addInterest.

Getters and Setters

In JavaScript, getters and setters provide a way to access and mutate private attributes indirectly while maintaining control over how values are retrieved or changed. This remains in line with the encapsulation principle by providing a controlled interface to interact with private data.

Getters

Getters allow access to the value of a private attribute in a safe, controlled manner. Here's how you can define a getter for a private attribute:

JavaScript
1class BankAccount { 2 #balance; // Declare a private field 3 4 constructor(accountNumber, balance) { 5 this.accountNumber = accountNumber; 6 this.#balance = balance; // Initialize the private attribute 7 } 8 9 // Getter for balance 10 get balance() { 11 return this.#balance; 12 } 13 14 deposit(amount) { 15 this.#balance += amount; // Deposit money 16 } 17} 18 19const bankAccount = new BankAccount(1234, 100); 20console.log(bankAccount.balance); // Works: logs 100

In this example, the balance getter method provides a safe way to access the private #balance attribute.

Setters

Setters allow modification of the value of a private attribute while providing a controlled interface. Here’s how you can define a setter for a private attribute:

JavaScript
1class BankAccount { 2 #balance; // Declare a private field 3 4 constructor(accountNumber, balance) { 5 this.accountNumber = accountNumber; 6 this.#balance = balance; // Initialize the private attribute 7 } 8 9 // Getter for balance 10 get balance() { 11 return this.#balance; 12 } 13 14 // Setter for balance 15 set balance(amount) { 16 if (amount >= 0) { 17 this.#balance = amount; 18 } else { 19 console.log("Balance cannot be negative."); 20 } 21 } 22 23 deposit(amount) { 24 this.#balance += amount; // Deposit money 25 } 26} 27 28const bankAccount = new BankAccount(1234, 100); 29bankAccount.balance = 200; // Works: sets balance to 200 30console.log(bankAccount.balance); // Works: logs 200 31bankAccount.balance = -50; // Logs: "Balance cannot be negative."

In this example, the balance setter method ensures that the #balance is only set to non-negative values, adding a layer of validation.

By using getters and setters, you can add logic for validating or transforming data, making your class more flexible and secure while adhering to encapsulation principles.

Lesson Summary and Practice

Great job refreshing your understanding of encapsulation, private attributes, and private methods concepts in JavaScript! Correctly understanding and applying these foundational principles of OOP makes your code concise, robust, and secure.

Coming up next is hands-on practice. Keep up the good work — exciting exercises are just around the corner!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.