POO : Encapsulation et Polymorphisme en JavaScript (Node.js) – Guide détaillé
What is it and why? (a real-life analogy)
Imagine you are a chef in a restaurant. You have a refrigerator, a stove, knives, and ingredients. To cook a dish, you open the refrigerator, take out the ingredients, cut them, and fry them on the stove. All of this is the inner kitchen, which restaurant visitors do not see. They only see the finished dish on the plate.
Now imagine you are a restaurant visitor. You do not go into the kitchen, you do not touch the knives, and you do not turn on the stove. You simply place an order with the waiter: "Please bring me carbonara pasta." After a while, the finished dish is brought to you. You do not know exactly how it was prepared — it is hidden from you. You only interact with the result.
The same thing happens in programming. We have objects (like the chef) that have internal data (ingredients, recipes) and methods (cutting, frying). But as programmers, we do not want anyone external to be able to get inside the object and spoil its data. We want to be able to communicate with the object only through special public methods (like through the waiter).
This is encapsulation — hiding the internal structure of an object and providing only a safe interface for working with it.
And what is polymorphism? Imagine you have a universal remote control. It has a "Play" button. If you press it on the TV, a movie will start. If you press it on the music system, a song will play. If you press it on the game console, a game will start. The same command ("Play") works differently depending on which device it is applied to.
In programming, polymorphism is the ability of different objects to react differently to the same command. For example, we have a "Cat" class and a "Dog" class. Both have a "makeSound()" method. But the cat will say "Meow", and the dog will say "Woof". One command — different behavior.
💡 Remember the main point: Encapsulation is about "what you can touch and what you cannot". Polymorphism is about "one interface — many implementations".
Theory: how it works internally
Encapsulation: data protection
In JavaScript, encapsulation is implemented through closures and private fields (starting from ES2022). Let's break down what this means.
When you create an object, it has properties (data) and methods (functions). Without encapsulation, any property can be changed directly:
// BAD EXAMPLE — without encapsulation
let user = {
name: "Ivan",
password: "12345" // Anyone can read and change this!
};
// Anyone can do this:
user.password = "hacked"; // Danger!
console.log(user.password); // "hacked"
With encapsulation, we hide the password and provide access only through special methods:
// GOOD EXAMPLE — with encapsulation
function CreateUser(name, password) {
// Private variable — cannot be accessed from outside
let _password = password;
return {
name: name,
// Public method for checking the password
checkPassword(enteredPassword) {
return _password === enteredPassword;
},
// Public method for changing the password
changePassword(oldPassword, newPassword) {
if (_password === oldPassword) {
_password = newPassword;
return true;
}
return false;
}
};
}
let user = CreateUser("Ivan", "12345");
console.log(user._password); // undefined — not visible!
console.log(user.checkPassword("12345")); // true
user.changePassword("12345", "new_password"); // successful
Polymorphism: one interface — many implementations
Polymorphism in JavaScript is achieved through inheritance and method overriding. When a child class inherits a method from a parent class, it can override it — that is, write its own version.
Imagine we have a base class "Transport" with a "move()" method. A car will drive on the road, a boat will sail on water, an airplane will fly through the air. They all "move", but in different ways.
// Base class
class Transport {
move() {
console.log("Transport is moving");
}
}
// Child classes override the method
class Car extends Transport {
move() {
console.log("🚗 Driving on the road");
}
}
class Boat extends Transport {
move() {
console.log("🚤 Sailing on water");
}
}
class Airplane extends Transport {
move() {
console.log("✈️ Flying through the air");
}
}
// Polymorphism in action:
function startMovement(transport) {
transport.move(); // One command — different behavior
}
startMovement(new Car()); // 🚗 Driving on the road
startMovement(new Boat()); // 🚤 Sailing on water
startMovement(new Airplane()); // ✈️ Flying through the air
🔑 Key idea: The functionstartMovementdoes not know which specific transport it is working with. It simply calls themove()method, and each object decides for itself how to execute it.
Syntax in JavaScript (Node.js): all forms and variants
1. Encapsulation through closures (old way, before ES6)
// Factory function with closure
function Account(initialAmount) {
// Private variable — accessible only inside the function
let _balance = initialAmount;
// Return the ob