Introduction
Object-Oriented Programming (OOP) is foundational for building well-structured, modular, and reusable code. While JavaScript was initially procedural, ES6 and beyond introduced syntax for OOP, making it an ideal language for mastering both functional and object-oriented paradigms. This article covers the fundamental OOP concepts in JavaScript, including classes, inheritance, polymorphism, and abstraction, along with JavaScript-specific features like prototype inheritance and object composition.
Key Concepts of OOP in JavaScript
1.Encapsulation:
Encapsulation allows grouping data and methods within objects, restricting direct access to an object’s state. This keeps data protected from unintended modifications and allows controlled interaction.
class Car {
constructor(make, model) {
this.make = make;
this.model = model;
this._engineOn = false;
}
startEngine() {
this._engineOn = true;
console.log(`${this.make} ${this.model} engine started.`);
}
stopEngine() {
this._engineOn = false;
console.log(`${this.make} ${this.model} engine stopped.`);
}
}
const myCar = new Car("Toyota", "Corolla");
myCar.startEngine(); // Output: Toyota Corolla engine started.
2.Inheritance:
Inheritance enables creating child classes based on a parent class, allowing code reuse and defining hierarchies.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound.`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
const myDog = new Dog("Max");
myDog.speak(); // Output: Max barks.
3.Polymorphism:
Polymorphism lets different classes respond to the same function or method call. JavaScript achieves polymorphism via method overriding.
class Printer {
print() {
console.log("Printing document...");
}
}
class PDFPrinter extends Printer {
print() {
console.log("Printing PDF document...");
}
}
const printer = new Printer();
const pdfPrinter = new PDFPrinter();
printer.print(); // Printing document...
pdfPrinter.print(); // Printing PDF document...
4.Abstraction:
Abstraction simplifies complex systems by exposing only necessary parts. ES2020 introduced private fields with #
, allowing encapsulation in classes.
class Account {
#balance;
constructor(initialBalance) {
this.#balance = initialBalance;
}
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
const myAccount = new Account(1000);
myAccount.deposit(500);
console.log(myAccount.getBalance()); // Output: 1500
Prototype-Based Inheritance in JavaScript
JavaScript is prototype-based, meaning that objects can directly inherit from other objects rather than classes. This is achieved through prototypes, which are objects from which other objects inherit methods and properties.
function Vehicle(type) {
this.type = type;
}
Vehicle.prototype.start = function() {
console.log(`${this.type} is starting.`);
};
const car = new Vehicle("Car");
car.start(); // Car is starting.
Composition over Inheritance
Composition is an alternative to inheritance where instead of creating classes in a hierarchy, you create objects that contain smaller, reusable objects to achieve desired functionality.
const canFly = {
fly() {
console.log("Flying!");
}
};
const canSwim = {
swim() {
console.log("Swimming!");
}
};
function Fish(name) {
this.name = name;
}
Object.assign(Fish.prototype, canSwim);
const fish = new Fish("Nemo");
fish.swim(); // Swimming!
Advanced OOP Patterns in JavaScript
1. Factory Pattern:
The Factory Pattern is a design pattern where you create objects without specifying the exact class. It’s useful for encapsulating the creation logic of objects.
function createUser(name, role) {
return {
name,
role,
describe() {
console.log(`${this.name} is a ${this.role}`);
}
};
}
const admin = createUser("Alice", "Administrator");
admin.describe(); // Alice is an Administrator
2. Singleton Pattern:
Singleton is a design pattern where a class has only one instance. It’s useful for creating globally accessible objects like configurations or application states.
const Singleton = (function () {
let instance;
function createInstance() {
return new Object("I am the instance");
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true
3. Observer Pattern:
The Observer Pattern defines a dependency relationship where changes in one object (subject) lead to notifications for other objects (observers).
class Subject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
update(data) {
console.log("Received update:", data);
}
}
const subject = new Subject();
const observer1 = new Observer();
subject.subscribe(observer1);
subject.notify("New Data Available"); // Received update: New Data Available
Challenges and Best Practices
1. Avoiding Inheritance Overuse: Favor composition for better flexibility and reuse.
2. Minimizing Side Effects: Keep data encapsulated to prevent unintended changes.
3. Using Object.freeze: This prevents accidental modifications in immutable objects.
At The End
JavaScript’s approach to OOP provides a flexible, hybrid model combining prototype-based inheritance and classical OOP. With ES6+ advancements like classes and private fields, JavaScript allows developers to build complex applications while maintaining a clean code structure. By mastering OOP in JavaScript, you can build scalable, maintainable, and performant code for real-world applications.
My personal website: https://shafayet.zya.me
A meme for you😉😉😉
Top comments (0)