Learn JavaScript (Object Oriented Programming) OOPs


Before the Object Oriented we use the Procedural programmingProcedural
Programming is a programming paradigm that follows a step-by-step approach using procedures (functions) to organize and execute code. It is based on the concept of procedures (or routines) that perform specific tasks.
classes and Objects
objcets are entites (an entity is an independent concept that hash its own properties and behaviors,like a person,a place an idea)
class is like a blueprint of the entities e.g : schema of a person is class and actual person with the properties is objects
OOPs Pillers
- encapsulation (reduce the no of parameters) -> This approach restricts external access to certain components of an object, promoting modularity and safeguarding data integrity.
class Car {
#engineStatus = false; // Private property
startEngine() {
this.#engineStatus = true;
console.log('Engine started.');
}
stopEngine() {
this.#engineStatus = false;
console.log('Engine stopped.');
}
getEngineStatus() {
return this.#engineStatus ? 'Engine is running.' : 'Engine is off.';
}
}
const myCar = new Car();
myCar.startEngine(); // Output: Engine started.
console.log(myCar.getEngineStatus()); // Output: Engine is running.
myCar.stopEngine(); // Output: Engine stopped.
console.log(myCar.getEngineStatus()); // Output: Engine is off.
2.abstraction ->Abstraction focuses on exposing only the essential features of an object while hiding the complex details. This simplifies the interaction with objects by providing a clear and simplified interface
class CoffeeMachine {
#waterLevel = 0; // Private property
fillWater(amount) {
this.#waterLevel += amount;
console.log(`Water level: ${this.#waterLevel}ml`);
}
makeCoffee() {
if (this.#waterLevel > 0) {
console.log('Making coffee...');
this.#waterLevel -= 50;
} else {
console.log('Please add water.');
}
}
cuurentWaterLevel(){
console.log(`The current water level is ${this.waterLevel}ml`)
}
}
const myCoffeeMachine = new CoffeeMachine();
myCoffeeMachine.fillWater(100); // Output: Water level: 100ml
myCoffeeMachine.makeCoffee(); // Output: Making coffee...
myCoffeeMachine.makeCoffee(); // Output: Making coffee...
myCoffeeMachine.makeCoffee(); // Output: Please add water.
- inheritence -> Inheritance allows a class (subclass or child class) to acquire properties and methods from another class (superclass or parent class). This promotes code reusability and establishes hierarchical relationships between classes.
class Animal{
constructor(name){
this.name = name;
}
speak(){
console.log(this.name,"make sound")
}
}
class Dog extends Animal{
speak(){
console.log(this.name," barks")
}
}
const myDog = new Dog('Buddy');
myDog.speak(); // Output: Buddy barks.
- polymorphisam -> Polymorphism enables objects of different classes to be treated as instances of the same class through a common interface. It allows methods to perform different tasks based on the object that invokes them.
class Shape {
area() {
console.log('Calculating area...');
}
}
class Rectangle extends Shape {
constructor(width, height) {
super();
this.width = width;
this.height = height;
}
area() {
return this.width * this.height;
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
area() {
return Math.PI * this.radius ** 2;
}
}
const shapes = [new Rectangle(10, 5), new Circle(7)];
shapes.forEach((shape) => {
console.log(shape.area());
});
// Output:
// 50
// 153.93804002589985
Encapsulation: Combining related data and methods within a single unit (class) to restrict external access and modification.
Abstraction: Simplifying complex systems by exposing only essential features and hiding implementation details.
Inheritance: Creating new classes that derive properties and behaviors from existing classes, promoting code reuse.
Polymorphism: Allowing objects to be treated as instances of their parent class, enabling a single interface to represent different underlying forms.
Specifically in JavaScript we have constructor ,later the class came to the picture
Lets create a constructor bank Account that take two input
function BankAccount(customerName, balance = 0) {
this.customerName = customerName;
// Generate a random account number
this.accountName = Math.round(Math.random() * 10000000000);
this.balance = balance;
this.deposit = function(amount) {
this.balance += amount;
};
this.withDraw = function(amount) {
if (amount > this.balance) {
console.log("Insufficient Balance");
return;
}
this.balance -= amount;
};
}
// lets make a new instance
let holder = new BankAccount('boss',123)
holder.deposit(100)
This will keep the functionality in memory for the every instance this
now to optimize this issue we can create a seperate prototype for the constructor and that can be used by the instences
function BankAccount(customerName, balance = 0) {
this.customerName = customerName;
// Generate a random account number
this.accountName = Math.round(Math.random() * 10000000000);
this.balance = balance;
}
// prototype will all to create functions once and it can be reused angain and again by instences , without cretaing each time ,which will take more space in memory
BankAccount.prototype.BankAccount = "This is a bank account constructor";
BankAccount.prototype.deposit = function(amount) {
this.balance += amount;
};
BankAccount.prototype.withDraw = function(amount) {
if (amount > this.balance) {
console.log("Insufficient Balance");
return;
}
this.balance -= amount;
};
// lets make a new instance
let holder = new BankAccount('boss',123)
holder.deposit(100)
console.log(holder.prototype)
it works same way but it is more memory efficient
Then we got introduced the class in javascript with automate the prototype process
class BankAccount {
customerName;
// Generate a random account number
accountName = Math.round(Math.random() * 10000000000);
balance;
constructor(name,balance=0){
this.customerName = name;
this.balance = balance;
}
deposit(amount) {
this.balance += amount;
};
withDraw(amount) {
if (amount > this.balance) {
console.log("Insufficient Balance");
return;
}
this.balance -= amount;
};
}
// lets make a new instance
let holder = new BankAccount('boss',123)
holder.deposit(100)
To make code reusable we can use the inheritance ,in which the parent code can be used by the child by using extends
keyword to send the data to the parent constructor we use the super
keyword and pass the parameter
class BankAccount {
customerName;
// Generate a random account number
accountName = Math.round(Math.random() * 10000000000);
balance;
constructor(name,balance=0){
this.customerName = name;
this.balance = balance;
}
deposit(amount) {
this.balance += amount;
};
withDraw(amount) {
if (amount > this.balance) {
console.log("Insufficient Balance");
return;
}
this.balance -= amount;
};
}
class CurretAccount extends BankAccount{
transactionLimit = 50000;
constructor(name, balance =0){
super(name,balance)
}
TakebussinusseLone(amount){
console.log(amount," loan taken")
}
}
class SavingsAccount extends BankAccount{
transactionLimit = 10000;
constructor(name,balance =0){
super(name,balance)
}
personalLoan(amount){
to make the make the variable and functions private we use #
in front of the variable and to access we use get
getter functions and set
setter functions
class BankAccount {
customerName;
// Generate a random account number
accountName = Math.round(Math.random() * 10000000000);
#balance;
constructor(name,balance=0){
this.customerName = name;
this.balance = balance;
}
deposit(amount) {
this.#balance += amount;
};
withDraw(amount) {
if (amount > this.balance) {
console.log("Insufficient Balance");
return;
}
this.#balance -= amount;
};
// tradetional methods
getBalance() {
return this.#balance;
}
setBalance(amount){
this.#balance = amount;
}
// useage of the getter and setter
set balance(amount){
if(isNaN(amount)){
throw new Error("it is not a valid amount")
}
return this.#balance = amount;
}
get balance(){
return this.#balance;
}
}
class CurretAccount extends BankAccount{
transactionLimit = 50000;
constructor(name, balance =0){
super(name,balance)
}
#calculateTheIntrest(){
console.log("ya it is done");
}
TakebussinusseLone(amount){
this.#calculateTheIntrest()
console.log(amount," loan taken")
}
}
//------------------------- BankAccount
let user = new BankAccount("Gopal",5362)
user.balance = 1837;
console.log(user.balance)
// tradetional methods
// console.log(user.setBalance(100000))
// console.log(user.getBalance())
// ------------------------ CurretAccount
// let rich = new CurretAccount("mano",10000)
// rich.deposit(100000)
// console.log(rich)
useing static
keyword
it is used to keep the value same for class and it will not created for each instence ,it willbe same for all instence ,and we can make operations on them
to access them we can the class name directly insted of creating instance
eg: Math.min(a,b)
insted of let math = new Math(); math.min(a,b)
// utility functions
// config properties
// class config{
// static secret = "secret";
// static password = "this d y7816377e%%&&(*&^$%)";
// static api = "^&*(&(*6797^97*(7)))";
// }
// let some = new config()
// console.log(config.password)
class User {
static id = 1;
constructor(name,age,income ){
this.name = name;
this.age = age;
this.income = income
this.id = User.id++;
}
static{
console.log("Initalized the process")
}
static compareAge(user1,user2){
return user1.age - user2.age;
}
static ComplareSalary(user1,user2){
return user1.income - user2.income
}
static Sum(a,b){
return a+b;
}
fun(){
console.log("having fun.:...:")
}
static masterOfFun(){
fun
}
}
let user1 = new User("Manu",12,121)
let user2 = new User("AManu",99,111)
let user3 = new User("Sam",66,679)
let users = [user1,user2,user3]
users.sort(User.compareAge) // just giving the reference
users.sort(User.ComplareSalary) // just give the reference
console.log(users)
console.log(User.Sum(1,2))
user1.masterOfFun()
User.masterOfFun()
// staic can directuly called using the class name
It can be used to increment the ids , or for any of the method to access data directly
โโ
lets do a hands on project on this
Lets make library management system for the college
1. Library Management System ๐
Project Overview
We need a Library Management System where users (students, teachers) can borrow, return, and search for books. The system should maintain book records, user details, and borrowing history.
Project Requirements
1๏ธโฃ User Roles & Responsibilities
Librarian
Add new books.
Remove books.
View the list of books and borrowers.
User (Student/Teacher)
Search for books.
Borrow books (with a return deadline).
Return books.
View borrowing history.
2๏ธโฃ Core Entities (Classes & Properties)
Book
title
(string)author
(string)ISBN
(unique string)availableCopies
(number)
User (Parent class for both Students & Teachers)
name
(string)userID
(unique string)borrowedBooks
(list of borrowed books)
Student (inherits from User)
studentID
(string)
Teacher (inherits from User)
teacherID
(string)
Library
books
(array ofBook
objects)users
(array ofUser
objects)Methods:
addBook()
โ Add a new book.removeBook()
โ Remove a book.searchBook(title/author/ISBN)
โ Search books by keyword.borrowBook(userID, bookISBN)
โ Allow a user to borrow a book.returnBook(userID, bookISBN)
โ Handle book returns.displayBooks()
โ Show all available books.
3๏ธโฃ System Functionalities & Constraints
โ
A user can borrow a maximum of 3 books at a time.
โ
The borrowing period is 14 days, after which a fine is applied.
โ
The system should track due dates and notify users of late returns.
โ
If a book is not available, the user should get a "Book Unavailable" message.
this is the basic idea to make this project
class Book {
constructor(title, author, ISBN, availableCopies){
this.title = title;
this.author = author;
this.ISBN = ISBN;
this.availableCopies = availableCopies;
}
}
class User {
constructor(name, UserID){
this.name = name;
this.UserID = UserID;
this.borrowedBooks = [];
}
canBorrow(){
return this.borrowedBooks.length < 3;
}
borrowedBooks(book){
if(this.canBorrow() && book.availableCopies > 0){
this.borrowedBooks.push({
book,
dueDate:new Date(Date.now()+ 14*24*60*60*1000),
});
book.availableCopies--;
console.log(`${this.name} borrowed "${book.title}". Due date: ${this.borrowedBooks[this.borrowedBooks.length - 1].dueDate}`);
} else {
console.log(this.canBorrow() ? `${book.title} is not available`:`Borrow limit reached`)
}
}
returnBook(bookISBN){
const index = this.borrowedBooks.findIndex(entry => entry.book.ISBN ===bookISBN)
if(index!==-1){
let returnedBook = this.borrowedBooks.splice(index, 1)[0].book;
returnedBook.availableCopies++;
console.log(`${this.name} returned "${returnedBook.title}".`);
} else {
console.log(`Book not found in ${this.name}'s borrowed list.`);
}
}
viewBorrowingHistory() {
if (this.borrowedBooks.length === 0) {
console.log(`${this.name} has no borrowed books.`);
} else {
console.log(`Borrowing history for ${this.name}:`);
this.borrowedBooks.forEach(entry => {
console.log(`- ${entry.book.title} (Due: ${entry.dueDate})`);
});
}
}
}
class Student extends User {
constructor(name,StudentId){
super(name,StudentId)
this.StudentId = StudentId;
}
}
class Teacher extends User {
constructor(name,teacherId){
super(name,teacherId)
this.teacherId = teacherId;
}
}
class Library {
constructor() {
this.books = [];
this.users = [];
}
// Add a book to the library
addBook(title, author, ISBN, availableCopies) {
const book = new Book(title, author, ISBN, availableCopies);
this.books.push(book);
console.log(`Book "${title}" added to the library.`);
}
removeBook(ISBN) {
const index = this.books.findIndex(book => book.ISBN === ISBN);
if (index !== -1) {
console.log(`Book "${this.books[index].title}" removed from the library.`);
this.books.splice(index, 1);
} else {
console.log(`Book with ISBN ${ISBN} not found.`);
}
}
searchBook(query) {
const results = this.books.filter(book =>
book.title.toLowerCase().includes(query.toLowerCase()) ||
book.author.toLowerCase().includes(query.toLowerCase()) ||
book.ISBN.includes(query)
);
if (results.length > 0) {
console.log(`Search results for "${query}":`);
results.forEach(book => console.log(`- ${book.title} by ${book.author} (ISBN: ${book.ISBN}, Copies: ${book.availableCopies})`));
} else {
console.log(`No books found matching "${query}".`);
}
}
addUser(user) {
this.users.push(user);
console.log(`User "${user.name}" added to the library.`);
}
// Borrow a book
borrowBook(userID, bookISBN) {
const user = this.users.find(user => user.userID === userID);
const book = this.books.find(book => book.ISBN === bookISBN);
if (user && book) {
user.borrowBook(book);
} else {
console.log("User or Book not found.");
}
}
returnBook(userID, bookISBN) {
const user = this.users.find(user => user.userID === userID);
if (user) {
user.returnBook(bookISBN);
} else {
console.log("User not found.");
}
}
// Display all books
displayBooks() {
if (this.books.length === 0) {
console.log("No books available in the library.");
} else {
console.log("Available books:");
this.books.forEach(book => {
console.log(`- ${book.title} by ${book.author} (ISBN: ${book.ISBN}, Copies: ${book.availableCopies})`);
});
}
}
}
const myLibrary = new Library();
// Adding Books
myLibrary.addBook("The Great Gatsby", "F. Scott Fitzgerald", "12345", 3);
myLibrary.addBook("1984", "George Orwell", "67890", 2);
myLibrary.addBook("To Kill a Mockingbird", "Harper Lee", "54321", 1);
const student1 = new Student("Alice", "S001");
const teacher1 = new Teacher("Mr. Smith", "T001");
myLibrary.addUser(student1);
myLibrary.addUser(teacher1);
myLibrary.displayBooks();
myLibrary.searchBook("1984");
// Borrowing books
myLibrary.borrowBook("S001", "12345"); // Alice borrows "The Great Gatsby"
myLibrary.borrowBook("S001", "67890"); // Alice borrows "1984"
myLibrary.borrowBook("S001", "54321"); // Alice borrows "To Kill a Mockingbird"
// Trying to exceed borrowing limit
myLibrary.borrowBook("S001", "67890"); // S
// Returning a book
myLibrary.returnBook("S001", "12345");
// Viewing borrowing history
student1.viewBorrowingHistory();
// Display books after transactions
myLibrary.displayBooks();
If yo need quick copy and see the output
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Banking System</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f0f2f5;
}
.section {
background: white;
padding: 20px;
margin-bottom: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
input, select, button {
margin: 5px;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
#output {
background: white;
padding: 20px;
border-radius: 8px;
min-height: 100px;
margin-top: 20px;
white-space: pre-wrap;
}
</style>
</head>
<body>
<h1>Banking System</h1>
<!-- Create Account Section -->
<div class="section">
<h2>Create New Account</h2>
<input type="text" id="holderName" placeholder="Holder Name">
<select id="accountType">
<option value="Savings">Savings Account</option>
<option value="Current">Current Account</option>
</select>
<button onclick="createAccount()">Create Account</button>
</div>
<!-- Deposit Section -->
<div class="section">
<h2>Deposit Money</h2>
<input type="text" id="depositAccount" placeholder="Account Number">
<input type="number" id="depositAmount" placeholder="Amount">
<button onclick="deposit()">Deposit</button>
</div>
<!-- Withdraw Section -->
<div class="section">
<h2>Withdraw Money</h2>
<input type="text" id="withdrawAccount" placeholder="Account Number">
<input type="number" id="withdrawAmount" placeholder="Amount">
<button onclick="withdraw()">Withdraw</button>
</div>
<!-- Transfer Section -->
<div class="section">
<h2>Transfer Money</h2>
<input type="text" id="fromAccount" placeholder="From Account">
<input type="text" id="toAccount" placeholder="To Account">
<input type="number" id="transferAmount" placeholder="Amount">
<button onclick="transfer()">Transfer</button>
</div>
<!-- Transaction History Section -->
<div class="section">
<h2>View Transactions</h2>
<input type="text" id="historyAccount" placeholder="Account Number">
<button onclick="viewTransactions()">Show History</button>
</div>
<!-- Output Display -->
<div id="output"></div>
<script>
// Include the original banking system code here
// ๐น Class representing a single Transaction
class Transaction {
constructor(transactionID, amount, type) {
this.transactionID = transactionID;
this.amount = amount;
this.type = type;
this.timestamp = new Date();
}
}
// ๐น Base Account Class
class Account {
constructor(accountNumber, holderName, balance = 0) {
this.accountNumber = accountNumber;
this.holderName = holderName;
this.balance = balance;
this.transactionHistory = [];
}
deposit(amount) {
if (amount > 0) {
this.balance += amount;
this.transactionHistory.push(new Transaction(this.generateTransactionID(), amount, "Deposit"));
console.log(`โน${amount} deposited to account ${this.accountNumber}. New Balance: โน${this.balance}`);
} else {
console.log("Deposit amount must be greater than zero.");
}
}
withdraw(amount) {
console.log("Withdraw method should be implemented in child classes.");
}
viewTransactions() {
console.log(`Transaction History for Account ${this.accountNumber}:`);
if (this.transactionHistory.length === 0) {
console.log("No transactions found.");
} else {
this.transactionHistory.forEach((t, index) => {
console.log(`${index + 1}. ${t.type} โน${t.amount} on ${t.timestamp}`);
});
}
}
generateTransactionID() {
return `${this.accountNumber}-${Date.now()}`;
}
}
// ๐น Savings Account Class
class SavingsAccount extends Account {
constructor(accountNumber, holderName, balance = 0, interestRate = 4, withdrawLimit = 10) {
super(accountNumber, holderName, balance);
this.interestRate = interestRate;
this.withdrawLimit = withdrawLimit;
this.withdrawCount = 0;
}
withdraw(amount) {
if (this.withdrawCount >= this.withdrawLimit) {
console.log(`Withdrawal limit reached for account ${this.accountNumber}.`);
} else if (amount > 0 && this.balance - amount >= 1000) {
this.balance -= amount;
this.withdrawCount++;
this.transactionHistory.push(new Transaction(this.generateTransactionID(), amount, "Withdrawal"));
console.log(`โน${amount} withdrawn from account ${this.accountNumber}. Remaining Balance: โน${this.balance}`);
} else {
console.log(`Insufficient balance or minimum balance restriction applied.`);
}
}
}
// ๐น Current Account Class
class CurrentAccount extends Account {
constructor(accountNumber, holderName, balance = 0, overdraftLimit = 50000) {
super(accountNumber, holderName, balance);
this.overdraftLimit = overdraftLimit;
}
withdraw(amount) {
if (this.balance - amount >= -this.overdraftLimit) {
this.balance -= amount;
this.transactionHistory.push(new Transaction(this.generateTransactionID(), amount, "Withdrawal"));
console.log(`โน${amount} withdrawn from account ${this.accountNumber}. New Balance: โน${this.balance}`);
} else {
console.log(`Withdrawal denied! Overdraft limit exceeded.`);
}
}
}
// ๐น Bank Class
class Bank {
constructor() {
this.accounts = [];
}
createAccount(holderName, type) {
const accountNumber = `AC${Math.floor(Math.random() * 1000000)}`;
let newAccount;
if (type === "Savings") {
newAccount = new SavingsAccount(accountNumber, holderName, 1000);
} else if (type === "Current") {
newAccount = new CurrentAccount(accountNumber, holderName);
} else {
console.log("Invalid account type.");
return;
}
this.accounts.push(newAccount);
console.log(`New ${type} Account created for ${holderName}. Account Number: ${accountNumber}`);
return newAccount;
}
findAccount(accountNumber) {
return this.accounts.find(acc => acc.accountNumber === accountNumber);
}
deposit(accountNumber, amount) {
const account = this.findAccount(accountNumber);
if (account) {
account.deposit(amount);
} else {
console.log(`Account ${accountNumber} not found.`);
}
}
withdraw(accountNumber, amount) {
const account = this.findAccount(accountNumber);
if (account) {
account.withdraw(amount);
} else {
console.log(`Account ${accountNumber} not found.`);
}
}
transfer(fromAccountNumber, toAccountNumber, amount) {
const fromAccount = this.findAccount(fromAccountNumber);
const toAccount = this.findAccount(toAccountNumber);
if (!fromAccount || !toAccount) {
console.log("One or both accounts not found.");
return;
}
if (amount > 0 && (fromAccount.balance - amount >= -fromAccount.overdraftLimit)) {
fromAccount.withdraw(amount);
toAccount.deposit(amount);
console.log(`โน${amount} transferred from ${fromAccountNumber} to ${toAccountNumber}`);
} else {
console.log("Insufficient balance or overdraft limit exceeded.");
}
}
viewTransactions(accountNumber) {
const account = this.findAccount(accountNumber);
if (account) {
account.viewTransactions();
} else {
console.log(`Account ${accountNumber} not found.`);
}
}
}
// Initialize bank
const myBank = new Bank();
// Redirect console.log to output div
const outputDiv = document.getElementById('output');
const oldLog = console.log;
console.log = function(message) {
oldLog.apply(console, arguments);
outputDiv.innerHTML += message + '\n';
};
// UI Functions
function createAccount() {
const holderName = document.getElementById('holderName').value;
const type = document.getElementById('accountType').value;
myBank.createAccount(holderName, type);
}
function deposit() {
const accountNumber = document.getElementById('depositAccount').value;
const amount = parseFloat(document.getElementById('depositAmount').value);
myBank.deposit(accountNumber, amount);
}
function withdraw() {
const accountNumber = document.getElementById('withdrawAccount').value;
const amount = parseFloat(document.getElementById('withdrawAmount').value);
myBank.withdraw(accountNumber, amount);
}
function transfer() {
const fromAccount = document.getElementById('fromAccount').value;
const toAccount = document.getElementById('toAccount').value;
const amount = parseFloat(document.getElementById('transferAmount').value);
myBank.transfer(fromAccount, toAccount, amount);
}
function viewTransactions() {
const accountNumber = document.getElementById('historyAccount').value;
myBank.viewTransactions(accountNumber);
}
</script>
</body>
</html>
Subscribe to my newsletter
Read articles from Gopal N D directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
