Practice JavaScript OOP with 3 real-world projects: Shopping Cart, Library System, and Student Grade Manager. Apply classes, inheritance, encapsulation, and polymorphism.
The best way to master Object-Oriented Programming is to build real projects that apply all four pillars: Encapsulation, Inheritance, Abstraction, and Polymorphism. This chapter gives you three complete, runnable projects of increasing complexity.
🎯 Project 2 — Library Management System
Concepts used: Inheritance, polymorphism, encapsulation, static methods
class LibraryItem {
#available = true;
#borrowedBy = null;
constructor(id, title, year) {
this.id = id;
this.title = title;
this.year = year;
}
// Abstract-like method — subclasses should override
getDetails() {
return `[${this.constructor.name}] "${this.title}" (${this.year})`;
}
checkout(memberName) {
if (!this.#available) throw new Error(`"${this.title}" is already checked out`);
this.#available = false;
this.#borrowedBy = memberName;
console.log(`✅ "${this.title}" checked out to ${memberName}`);
return this;
}
returnItem() {
if (this.#available) throw new Error(`"${this.title}" is not checked out`);
const borrower = this.#borrowedBy;
this.#available = true;
this.#borrowedBy = null;
console.log(`📬 "${this.title}" returned by ${borrower}`);
return this;
}
get isAvailable() { return this.#available; }
get borrowedBy() { return this.#borrowedBy; }
}
class Book extends LibraryItem {
constructor(id, title, author, year, pages) {
super(id, title, year);
this.author = author;
this.pages = pages;
}
getDetails() {
return `📚 Book: "${this.title}" by ${this.author} (${this.year}) — ${this.pages} pages`;
}
}
class DVD extends LibraryItem {
constructor(id, title, director, year, duration) {
super(id, title, year);
this.director = director;
this.duration = duration;
}
getDetails() {
return `🎬 DVD: "${this.title}" dir. ${this.director} (${this.year}) — ${this.duration}min`;
}
}
class Library {
#items = new Map();
#members = new Set();
static instance = null; // Singleton pattern
static getInstance() {
if (!Library.instance) Library.instance = new Library();
return Library.instance;
}
addItem(item) { this.#items.set(item.id, item); return this; }
addMember(name) { this.#members.add(name); return this; }
checkout(itemId, memberName) {
if (!this.#members.has(memberName)) throw new Error(`${memberName} is not a member`);
const item = this.#items.get(itemId);
if (!item) throw new Error(`Item ${itemId} not found`);
item.checkout(memberName);
}
returnItem(itemId) {
const item = this.#items.get(itemId);
if (!item) throw new Error(`Item ${itemId} not found`);
item.returnItem();
}
catalog() {
console.log("\n📖 Library Catalog:");
for (const item of this.#items.values()) {
const status = item.isAvailable ? "Available" : `Out — ${item.borrowedBy}`;
console.log(` [${item.id}] ${item.getDetails()} — ${status}`);
}
}
}
// --- Run the project ---
const lib = Library.getInstance();
lib.addMember("Riya").addMember("Aman");
lib.addItem(new Book(1, "JavaScript: The Good Parts", "Douglas Crockford", 2008, 176));
lib.addItem(new Book(2, "You Don't Know JS", "Kyle Simpson", 2015, 278));
lib.addItem(new DVD(3, "The Social Network", "David Fincher", 2010, 120));
lib.checkout(1, "Riya");
lib.checkout(3, "Aman");
lib.catalog();
lib.returnItem(1);
lib.catalog();
✅ "JavaScript: The Good Parts" checked out to Riya
✅ "The Social Network" checked out to Aman
📖 Library Catalog:
[1] 📚 Book: "JavaScript: The Good Parts" by Douglas Crockford (2008) — 176 pages — Out — Riya
[2] 📚 Book: "You Don't Know JS" by Kyle Simpson (2015) — 278 pages — Available
[3] 🎬 DVD: "The Social Network" dir. David Fincher (2010) — 120min — Out — Aman
📬 "JavaScript: The Good Parts" returned by Riya
📖 Library Catalog:
[1] 📚 Book: "JavaScript: The Good Parts" by Douglas Crockford (2008) — 176 pages — Available
[2] 📚 Book: "You Don't Know JS" by Kyle Simpson (2015) — 278 pages — Available
[3] 🎬 DVD: "The Social Network" dir. David Fincher (2010) — 120min — Out — Aman
🎯 Project 3 — Student Grade Manager
Concepts used: Classes, private methods, sorting, aggregation, report generation
class Grade {
constructor(subject, score, maxScore = 100) {
this.subject = subject;
this.score = score;
this.maxScore = maxScore;
}
get percentage() { return (this.score / this.maxScore) * 100; }
get letter() {
const p = this.percentage;
if (p >= 90) return "A";
if (p >= 80) return "B";
if (p >= 70) return "C";
if (p >= 60) return "D";
return "F";
}
}
class Student {
#grades = [];
#id;
constructor(id, name, rollNumber) {
this.#id = id;
this.name = name;
this.rollNumber = rollNumber;
}
addGrade(subject, score, maxScore = 100) {
this.#grades.push(new Grade(subject, score, maxScore));
return this;
}
get id() { return this.#id; }
get grades() { return [...this.#grades]; }
get average() {
if (!this.#grades.length) return 0;
const sum = this.#grades.reduce((acc, g) => acc + g.percentage, 0);
return sum / this.#grades.length;
}
get overallGrade() { return new Grade("Overall", this.average).letter; }
report() {
console.log(`\n📋 Student: ${this.name} (Roll: ${this.rollNumber})`);
console.log("─".repeat(45));
this.#grades
.sort((a, b) => b.percentage - a.percentage)
.forEach(g => {
console.log(` ${g.subject.padEnd(20)} ${g.score}/${g.maxScore} (${g.percentage.toFixed(1)}%) — ${g.letter}`);
});
console.log("─".repeat(45));
console.log(` Average: ${this.average.toFixed(1)}% | Grade: ${this.overallGrade}`);
}
}
class ClassRoom {
#students = [];
enroll(student) { this.#students.push(student); return this; }
topStudents(n = 3) {
return [...this.#students]
.sort((a, b) => b.average - a.average)
.slice(0, n);
}
classReport() {
this.#students.forEach(s => s.report());
const avg = this.#students.reduce((a, s) => a + s.average, 0) / this.#students.length;
console.log(`\n🏆 Top Student: ${this.topStudents(1)[0].name}`);
console.log(`📊 Class Average: ${avg.toFixed(1)}%`);
}
}
// --- Run the project ---
const classroom = new ClassRoom();
const riya = new Student(1, "Riya Sharma", "JS001");
riya.addGrade("JavaScript", 95)
.addGrade("HTML/CSS", 88)
.addGrade("React", 91)
.addGrade("Node.js", 85);
const aman = new Student(2, "Aman Gupta", "JS002");
aman.addGrade("JavaScript", 78)
.addGrade("HTML/CSS", 92)
.addGrade("React", 74)
.addGrade("Node.js", 80);
classroom.enroll(riya).enroll(aman);
classroom.classReport();
📋 Student: Riya Sharma (Roll: JS001)
─────────────────────────────────────────────
JavaScript 95/100 (95.0%) — A
React 91/100 (91.0%) — A
HTML/CSS 88/100 (88.0%) — B
Node.js 85/100 (85.0%) — B
─────────────────────────────────────────────
Average: 89.8% | Grade: B
📋 Student: Aman Gupta (Roll: JS002)
─────────────────────────────────────────────
HTML/CSS 92/100 (92.0%) — A
Node.js 80/100 (80.0%) — B
JavaScript 78/100 (78.0%) — C
React 74/100 (74.0%) — C
─────────────────────────────────────────────
Average: 81.0% | Grade: B
🏆 Top Student: Riya Sharma
📊 Class Average: 85.4%
🎯 Key Takeaways
- Project 1 (Shopping Cart) — demonstrates encapsulation (
#items, #coupon), method chaining, and real business logic - Project 2 (Library) — demonstrates inheritance (
Book, DVD extend LibraryItem), polymorphism (getDetails() varies by type), and the Singleton pattern - Project 3 (Grade Manager) — demonstrates nested classes, private methods, sorting, and aggregation
- Always apply the Single Responsibility Principle — each class does one thing well
- Use private fields to protect state that should not be directly mutated
- Method chaining (returning
this) makes APIs fluent and readable - OOP shines when your application has multiple entity types with relationships