Introduction to OOP
Object-Oriented Programming (OOP) is a programming paradigm centered around objects rather than functions and logic. OOP is crucial in JavaScript because it allows developers to create modular, reusable code. The four fundamental principles of OOP are:
Real-World Scenario: Online Shopping Platform
Imagine you are building an online shopping platform. You need to manage users, products, and orders. OOP allows you to structure your code in a way that each of these entities (users, products, orders) can be represented by objects, encapsulating their properties and behaviors.
Classes are blueprints for creating objects (instances). They encapsulate data and methods that operate on that data.
Objects are instances of classes. They hold specific values for properties and can execute methods defined in the class.
Real-World Example: User Management System
class User {
constructor(username, email) {
this.username = username;
this.email = email;
}
login() {
console.log(`${this.username} has logged in`);
}
logout() {
console.log(`${this.username} has logged out`);
}
}
const user1 = new User('john_doe', 'john@example.com');
user1.login(); // john_doe has logged in
In this example, User
is a class that defines a blueprint for user objects. Each user has a username
and email
, and can login
and logout
.
Methods are functions defined within a class. Properties are values associated with an object.
Real-World Example: E-commerce Product
class Product {
constructor(name, price) {
this.name = name;
this.price = price;
}
displayInfo() {
console.log(`Product: ${this.name}, Price: $${this.price}`);
}
}
const product = new Product('Laptop', 999.99);
product.displayInfo(); // Product: Laptop, Price: $999.99
Here, Product
is a class representing a product in an e-commerce platform, with properties name
and price
, and a method displayInfo
to show product details.
Inheritance allows a class to inherit properties and methods from another class using the extends
keyword.
Real-World Example: Animal Hierarchy
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
const dog = new Dog('Rex');
dog.speak(); // Rex barks.
In this example, Dog
inherits from Animal
, so Dog
instances have the name
property and speak
method from Animal
, but Dog
overrides the speak
method.
Encapsulation hides the internal state of an object and requires all interaction to be performed through an object’s methods.
Real-World Example: Bank Account
class BankAccount {
#balance;
constructor(accountNumber, balance) {
this.accountNumber = accountNumber;
this.#balance = balance;
}
deposit(amount) {
this.#balance += amount;
console.log(`Deposited: $${amount}. New balance: $${this.#balance}`);
}
withdraw(amount) {
if (amount > this.#balance) {
console.log('Insufficient funds');
return;
}
this.#balance -= amount;
console.log(`Withdrew: $${amount}. New balance: $${this.#balance}`);
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount('12345678', 1000);
account.deposit(500); // Deposited: $500. New balance: $1500
account.withdraw(200); // Withdrew: $200. New balance: $1300
Here, the BankAccount
class encapsulates the balance using a private field #balance
. Only the methods deposit
, withdraw
, and getBalance
can access and modify the balance, protecting it from direct manipulation.
Polymorphism allows methods to do different things based on the object it is acting upon.
Real-World Example: Payment Processing
class Payment {
process() {
console.log('Processing payment...');
}
}
class CreditCardPayment extends Payment {
process() {
console.log('Processing credit card payment...');
}
}
class PayPalPayment extends Payment {
process() {
console.log('Processing PayPal payment...');
}
}
const payments = [new Payment(), new CreditCardPayment(), new PayPalPayment()];
payments.forEach(payment => payment.process());
// Output:
// Processing payment...
// Processing credit card payment...
// Processing PayPal payment...
In this scenario, different types of payments (CreditCardPayment
, PayPalPayment
) can be processed using the same method process
, demonstrating polymorphism.
Abstraction hides the complex reality while exposing only the necessary parts.
Real-World Example: Coffee Machine
class CoffeeMachine {
#waterAmount = 0;
setWaterAmount(value) {
if (value < 0) throw new Error("Negative water");
this.#waterAmount = value;
}
getWaterAmount() {
return this.#waterAmount;
}
constructor(power) {
this._power = power;
console.log(`Created a coffee machine with power ${power}`);
}
#boilWater() {
console.log('Boiling water...');
}
makeCoffee() {
this.#boilWater();
console.log('Coffee is ready!');
}
}
const coffeeMachine = new CoffeeMachine(1000);
coffeeMachine.setWaterAmount(200);
coffeeMachine.makeCoffee(); // Boiling water... Coffee is ready!
In this example, the CoffeeMachine
class hides the complexity of boiling water and making coffee. The user interacts with the simpler interface: setting the water amount and making coffee.
For more insights on OOP concepts, refer to MDN Web Docs on Object-Oriented Programming.
Video Resources:
By understanding and applying these OOP principles, developers can write more efficient, maintainable, and scalable JavaScript code.