JavaScript has historically been a prototype-based language, but since ES6 introduced the class syntax, OOP in JavaScript has become far more intuitive and familiar to developers from C#, Java, or C++ backgrounds. While the class keyword is syntactic sugar over prototypes, it provides a clean and structured model for building scalable applications.
This article breaks down how OOP works in JavaScript using classes, covering the four pillars—Encapsulation, Abstraction, Inheritance, and Polymorphism—with practical, real-world examples.
Understanding JavaScript Classes
A JavaScript class is a template for creating objects. It bundles data (properties) and functions (methods) into a single unit.
Basic Class Example
class User {
constructor(name, role) {
this.name = name;
this.role = role;
}
getInfo() {
return `${this.name} is a ${this.role}`;
}
}
const u1 = new User("Abhishek", "Developer");
console.log(u1.getInfo());
Encapsulation — Binding Data and Methods
Encapsulation protects internal object data and exposes only what is necessary.
JavaScript supports public and private fields using the # prefix.
class BankAccount {
#balance = 0;
constructor(owner) {
this.owner = owner;
}
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
const acc = new BankAccount("Abhishek");
acc.deposit(1000);
console.log(acc.getBalance());
Here the balance is private and cannot be accessed outside the class.
Abstraction — Hiding Complex Logic
Abstraction means showing only the essential behavior and hiding internal complexity.
class Payment {
pay(amount) {
if (this.validateCard()) {
console.log(`Paid ₹${amount}`);
}
}
validateCard() {
console.log("Card validated");
return true;
}
}
User interacts only with pay(), not internal validation.
Inheritance — Extending Functionality
JavaScript supports inheritance using the extends keyword.
class Vehicle {
start() {
return "Vehicle starting...";
}
}
class Car extends Vehicle {
drive() {
return "Car is driving...";
}
}
const c = new Car();
console.log(c.start());
console.log(c.drive());
Polymorphism — Same Method, Different Behavior
Polymorphism allows child classes to override parent methods.
class Logger {
log(msg) {
console.log(`Log: ${msg}`);
}
}
class FileLogger extends Logger {
log(msg) {
console.log(`Writing to file: ${msg}`);
}
}
class DbLogger extends Logger {
log(msg) {
console.log(`Inserting log into DB: ${msg}`);
}
}
function doLog(logger) {
logger.log("Application started");
}
doLog(new FileLogger());
doLog(new DbLogger());
Static Methods — Utility Without Object Creation
Static methods belong to the class itself.
class MathHelper {
static add(a, b) {
return a + b;
}
}
console.log(MathHelper.add(5, 7));
Getters and Setters — Controlled Data Access
class Student {
constructor(name) {
this._name = name;
}
get name() {
return this._name.toUpperCase();
}
set name(val) {
if (val.length > 0) this._name = val;
}
}
const s = new Student("abhishek");
console.log(s.name);
s.name = "Rahul";
console.log(s.name);
Real-World Example: Product & Cart System
class Product {
constructor(id, name, price) {
this.id = id;
this.name = name;
this.price = price;
}
}
class Cart {
#items = [];
add(product) {
this.#items.push(product);
}
getTotal() {
return this.#items.reduce((t, p) => t + p.price, 0);
}
}
const p1 = new Product(1, "Protein", 1200);
const p2 = new Product(2, "Creatine", 900);
const cart = new Cart();
cart.add(p1);
cart.add(p2);
console.log("Total:", cart.getTotal());
Why Use OOP in JavaScript?
OOP makes JavaScript code:
More structured
More reusable
Closer to backend OOP languages like C#
Better for complex apps (React, Node.js services, game engines)
Summary
JavaScript’s class-based OOP is powerful despite being syntactic sugar over prototypes. It supports the full range of OOP concepts—encapsulation, abstraction, inheritance, and polymorphism—while allowing developers to write clean, maintainable, and scalable code. Whether you're building frontend UI systems or backend services in Node.js, OOP with classes remains a highly effective architectural approach.