Exploring Object-Oriented Programming (OOP) in JavaScript
Date: December 17, 2024
Object-Oriented Programming (OOP) is a paradigm that uses objects to model real-world entities. JavaScript, being a versatile programming language, provides robust support for OOP through its prototypes, ES6 classes, and modern enhancements. Today, we'll dive deep into the principles and features of OOP in JavaScript.
Core Concepts of OOP in JavaScript
1. Objects
Objects are the building blocks of OOP. In JavaScript, an object is a collection of key-value pairs.
Example: Creating Objects
const car = {
brand: "Toyota",
model: "Corolla",
start() {
return `${this.brand} ${this.model} is starting.`;
}
};
console.log(car.start()); // Output: Toyota Corolla is starting.
2. Classes
Classes are blueprints for creating objects. They encapsulate data and behavior. JavaScript introduced the class
keyword in ES6.
Example: Creating a Class
class Animal {
constructor(name, species) {
this.name = name;
this.species = species;
}
makeSound() {
return `${this.name} is making a sound.`;
}
}
const dog = new Animal("Buddy", "Dog");
console.log(dog.makeSound()); // Output: Buddy is making a sound.
3. Encapsulation
Encapsulation means bundling data and methods together while restricting direct access to some components. JavaScript achieves this using public, private, and protected members.
Private Fields
Private fields are denoted by a #
prefix and are accessible only within the class.
Example: Private Fields
class BankAccount {
#balance;
constructor(initialBalance) {
this.#balance = initialBalance;
}
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount(100);
account.deposit(50);
console.log(account.getBalance()); // Output: 150
// console.log(account.#balance); // Error: Private field '#balance' must be declared in an enclosing class
4. Inheritance
Inheritance allows one class to inherit properties and methods from another class using the extends
keyword.
Example: Inheritance
class Vehicle {
constructor(brand) {
this.brand = brand;
}
start() {
return `${this.brand} vehicle is starting.`;
}
}
class Car extends Vehicle {
constructor(brand, model) {
super(brand); // Calls the parent class constructor
this.model = model;
}
display() {
return `${this.brand} ${this.model} is ready to go.`;
}
}
const myCar = new Car("Tesla", "Model S");
console.log(myCar.display()); // Output: Tesla Model S is ready to go.
5. Polymorphism
Polymorphism allows a subclass to override a method from its parent class to provide a specific implementation.
Example: Method Overriding
class Shape {
area() {
return "Area is not defined.";
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
area() {
return Math.PI * this.radius ** 2;
}
}
const circle = new Circle(5);
console.log(circle.area()); // Output: 78.53981633974483
6. Abstraction
Abstraction focuses on exposing only essential details while hiding implementation complexities. While JavaScript doesn't have abstract classes natively, you can simulate them.
Example: Simulating Abstraction
class Animal {
constructor(name) {
if (this.constructor === Animal) {
throw new Error("Abstract class cannot be instantiated directly.");
}
this.name = name;
}
makeSound() {
throw new Error("Abstract method must be implemented.");
}
}
class Dog extends Animal {
makeSound() {
return "Bark!";
}
}
const dog = new Dog("Buddy");
console.log(dog.makeSound()); // Output: Bark!
// const animal = new Animal("Some Animal"); // Error: Abstract class cannot be instantiated directly.
7. Prototypes and Prototype Chain
JavaScript is a prototype-based language. Every object has an internal link to another object called its prototype.
Example: Prototype Chain
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
return `Hello, my name is ${this.name}.`;
};
const person1 = new Person("Alice");
console.log(person1.greet()); // Output: Hello, my name is Alice.
8. Object Composition vs. Inheritance
Instead of using inheritance, you can compose objects by combining functionalities. This approach avoids the complexities of deep inheritance hierarchies.
Example: Composition
const canDrive = {
drive() {
return "Driving a vehicle.";
}
};
const canFly = {
fly() {
return "Flying an aircraft.";
}
};
const pilot = Object.assign({}, canDrive, canFly);
console.log(pilot.drive()); // Output: Driving a vehicle.
console.log(pilot.fly()); // Output: Flying an aircraft.
Key Principles of OOP
- DRY (Don't Repeat Yourself): Reuse code through classes and inheritance.
- SOLID Principles: Follow best practices for writing scalable and maintainable OOP code.
Real-World Example: User Management System
Step 1: Define a Base Class
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
login() {
return `${this.name} has logged in.`;
}
}
Step 2: Extend Functionality
class Admin extends User {
deleteUser(user) {
return `${user.name} has been deleted by ${this.name}.`;
}
}
Step 3: Create Instances
const admin = new Admin("Admin Arjun", "admin@example.com");
const user = new User("Regular Parth", "Parth@example.com");
console.log(admin.login()); // Output: Admin Arjun has logged in.
console.log(admin.deleteUser(user)); // Output: Regular Parth has been deleted by Admin Arjun.
Practice Tasks
- Create a class hierarchy for a library management system.
- Implement a
BankAccount
class with private fields for balance and public methods for deposit and withdrawal. - Write a
Vehicle
class with subclasses likeCar
andBike
demonstrating polymorphism.
Conclusion
OOP in JavaScript provides a powerful way to write clean, modular, and reusable code. By mastering concepts like classes, inheritance, encapsulation, and polymorphism, you'll be well-equipped to build scalable applications. Keep experimenting and applying these concepts to real-world problems to solidify your understanding!
Tomorrow’s Topic: We’ll explore Asynchronous Programming in JavaScript, diving deep into callbacks, promises, and async/await. Stay tuned!
Top comments (0)