JavaScript Classes and Object-Oriented Programming

Object-Oriented Programming (OOP) is a way of organizing code around objects — entities that combine data (properties) and behavior (methods). JavaScript classes, introduced in ES6, provide a clean and readable syntax for creating objects using the OOP approach.

Real-life analogy: A class is like a blueprint for a house. Multiple houses can be built from the same blueprint, each with its own address and color, but all sharing the same structure and rooms.

What is a Class?

A class is a template (blueprint) for creating objects. Once a class is defined, multiple objects (called instances) can be created from it, each with their own data.

Defining a Class

class Animal {
  constructor(name, sound) {
    this.name  = name;
    this.sound = sound;
  }

  speak() {
    console.log(`${this.name} says ${this.sound}!`);
  }
}

let dog = new Animal("Dog", "Woof");
let cat = new Animal("Cat", "Meow");

dog.speak(); // Dog says Woof!
cat.speak(); // Cat says Meow!

Key Parts of a Class

PartDescription
class keywordDeclares the class
constructor()Special method that runs automatically when a new object is created
thisRefers to the current object being created
MethodsFunctions defined inside the class that describe behavior
new keywordCreates a new instance (object) from the class

The constructor Method

The constructor is automatically called when new ClassName() is used. It sets up the initial state of the object.

class Student {
  constructor(name, age, grade) {
    this.name  = name;
    this.age   = age;
    this.grade = grade;
  }

  introduce() {
    console.log(`Hi, I am ${this.name}, age ${this.age}, in Grade ${this.grade}.`);
  }
}

let s1 = new Student("Riya", 16, 10);
let s2 = new Student("Arjun", 17, 11);

s1.introduce(); // Hi, I am Riya, age 16, in Grade 10.
s2.introduce(); // Hi, I am Arjun, age 17, in Grade 11.

Class Methods

Methods are functions defined inside a class. They describe what an object can do.

class BankAccount {
  constructor(owner, balance) {
    this.owner   = owner;
    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.");
    } else {
      this.balance -= amount;
      console.log(`Withdrawn ₹${amount}. Remaining: ₹${this.balance}`);
    }
  }

  showBalance() {
    console.log(`${this.owner}'s balance: ₹${this.balance}`);
  }
}

let account = new BankAccount("Neha", 5000);
account.deposit(1000);    // Deposited ₹1000. New balance: ₹6000
account.withdraw(2500);   // Withdrawn ₹2500. Remaining: ₹3500
account.showBalance();    // Neha's balance: ₹3500

Getters and Setters

Getters and setters allow controlled access to object properties. A getter reads a value; a setter validates and sets a value.

class Temperature {
  constructor(celsius) {
    this._celsius = celsius;
  }

  get fahrenheit() {
    return (this._celsius * 9 / 5) + 32;
  }

  set celsius(value) {
    if (value < -273.15) {
      console.log("Temperature below absolute zero is not valid.");
    } else {
      this._celsius = value;
    }
  }

  get celsius() {
    return this._celsius;
  }
}

let temp = new Temperature(100);
console.log(temp.fahrenheit); // 212
temp.celsius = 37;
console.log(temp.celsius);    // 37
console.log(temp.fahrenheit); // 98.6

Static Methods

Static methods belong to the class itself — not to individual instances. They are called directly on the class name.

class MathHelper {
  static square(n) {
    return n * n;
  }

  static cube(n) {
    return n * n * n;
  }

  static isEven(n) {
    return n % 2 === 0;
  }
}

console.log(MathHelper.square(5));    // 25
console.log(MathHelper.cube(3));      // 27
console.log(MathHelper.isEven(8));    // true

// Static methods cannot be called on an instance:
// let m = new MathHelper();
// m.square(4); // TypeError

Inheritance — Extending a Class

Inheritance allows one class to extend another, reusing its properties and methods while adding or overriding specific behavior. The extends keyword creates the parent-child relationship.

class Vehicle {
  constructor(brand, speed) {
    this.brand = brand;
    this.speed = speed;
  }

  move() {
    console.log(`${this.brand} is moving at ${this.speed} km/h.`);
  }
}

class Car extends Vehicle {
  constructor(brand, speed, doors) {
    super(brand, speed); // Call parent constructor
    this.doors = doors;
  }

  describe() {
    console.log(`${this.brand} car with ${this.doors} doors, speed ${this.speed} km/h.`);
  }
}

let myCar = new Car("Toyota", 120, 4);
myCar.move();     // Toyota is moving at 120 km/h.
myCar.describe(); // Toyota car with 4 doors, speed 120 km/h.

The super Keyword

super() calls the parent class constructor. It must be called before using this in a child constructor.

Method Overriding

A child class can override a method from the parent class to provide different behavior.

class Shape {
  area() {
    return 0;
  }

  describe() {
    console.log(`Area: ${this.area()}`);
  }
}

class Circle extends Shape {
  constructor(radius) {
    super();
    this.radius = radius;
  }

  area() {
    return (Math.PI * this.radius * this.radius).toFixed(2);
  }
}

class Rectangle extends Shape {
  constructor(width, height) {
    super();
    this.width  = width;
    this.height = height;
  }

  area() {
    return this.width * this.height;
  }
}

let c = new Circle(7);
let r = new Rectangle(4, 6);

c.describe(); // Area: 153.94
r.describe(); // Area: 24

instanceof — Check Object Type

let c = new Circle(5);
console.log(c instanceof Circle);  // true
console.log(c instanceof Shape);   // true (inherits from Shape)
console.log(c instanceof Array);   // false

Private Fields (ES2022)

Private fields use the # prefix and cannot be accessed from outside the class.

class User {
  #password;

  constructor(username, password) {
    this.username = username;
    this.#password = password;
  }

  checkPassword(input) {
    return input === this.#password;
  }
}

let user = new User("admin", "secret123");
console.log(user.username);            // admin
console.log(user.checkPassword("secret123")); // true
// console.log(user.#password);        // SyntaxError — private!

Four Pillars of OOP

PillarMeaningJavaScript Feature
EncapsulationBundle data and methods together, hide internal detailsClasses, private fields (#)
AbstractionShow only what is necessary, hide complexityMethods, getters/setters
InheritanceChild class reuses parent class featuresextends, super
PolymorphismSame method name behaves differently in different classesMethod overriding

Practical Example — Library System

class Book {
  #checkedOut = false;

  constructor(title, author) {
    this.title  = title;
    this.author = author;
  }

  checkout() {
    if (this.#checkedOut) {
      console.log(`"${this.title}" is already checked out.`);
    } else {
      this.#checkedOut = true;
      console.log(`"${this.title}" checked out successfully.`);
    }
  }

  returnBook() {
    this.#checkedOut = false;
    console.log(`"${this.title}" returned. Thank you!`);
  }

  get status() {
    return this.#checkedOut ? "Checked Out" : "Available";
  }
}

let book1 = new Book("JavaScript Mastery", "Rohan Shah");
console.log(book1.status); // Available
book1.checkout();          // "JavaScript Mastery" checked out successfully.
book1.checkout();          // "JavaScript Mastery" is already checked out.
console.log(book1.status); // Checked Out
book1.returnBook();        // "JavaScript Mastery" returned. Thank you!
console.log(book1.status); // Available

Key Points to Remember

  • A class is a blueprint for creating objects with shared properties and methods
  • The constructor() runs automatically when a new instance is created with new
  • this refers to the current instance inside a class
  • Use extends for inheritance and super() to call the parent constructor
  • Static methods belong to the class, not to instances — called directly on the class name
  • Getters and setters provide controlled access to properties
  • Private fields (prefixed with #) are accessible only within the class
  • Method overriding allows child classes to redefine parent methods
  • Use instanceof to check if an object is an instance of a class

Leave a Comment

Your email address will not be published. Required fields are marked *