Skip to main content

Command Palette

Search for a command to run...

Understanding Object-Oriented Programming in JavaScript (OOP)

Learn how Object-Oriented Programming works in JavaScript using simple examples like classes, objects, constructors, and methods.

Updated
6 min read
Understanding Object-Oriented Programming in JavaScript (OOP)
S

Hi, I’m Shubham 👋 A learner sharing my journey in programming, tech, and self-growth. Learning every day and writing what I learn.

Object-Oriented Programming (OOP) helps you model real-world things in code. In JavaScript, OOP makes your programs more organized, reusable, and easier to maintain. This article explains the core ideas with simple analogies and small code examples so you can start using classes and objects right away.


Real-world analogy — blueprint → objects

Think of a blueprint (a car design) and the cars produced from it.

  • Blueprint = class

  • Car produced from blueprint = object (or instance)

  • Multiple cars can be created from the same blueprint, each with its own color, engine, and license plate.

This is the heart of OOP: a single definition (class) that produces many specific things (objects) that share behavior.


What does OOP mean (plain English)?

OOP is a programming style that organizes code around objects — entities that combine data (properties) and behaviour (methods). The main benefits:

  • Reusability — define behavior once in a class, create many objects.

  • Organization — group related data and functions together.

  • Abstraction — hide complex details behind simple interfaces.

  • Encapsulation — keep object internals private and expose only what’s needed.


What is a class in JavaScript?

A class is a template for creating objects. In modern JavaScript (ES6+), class syntax is a clearer way to declare constructor functions and prototypes.

Basic structure:

class Car {
  constructor(make, model, year) {
    this.make = make;     // property
    this.model = model;   // property
    this.year = year;     // property
  }

  start() {               // method
    console.log(`\({this.make} \){this.model} started.`);
  }

  info() {                // another method
    return `\({this.year} \){this.make} ${this.model}`;
  }
}
  • constructor is a special method called when you create an object with new.

  • this refers to the object being created (the instance).

  • Methods defined inside the class are shared by instances (under the hood via prototype).


Creating objects using classes (instantiation)

Use new to create an object (instance) from a class:

const car1 = new Car("Toyota", "Corolla", 2020);
const car2 = new Car("Tesla", "Model 3", 2022);

car1.start();                // Toyota Corolla started.
console.log(car2.info());    // 2022 Tesla Model 3

Each object has its own properties:

  • car1.make === "Toyota"

  • car2.make === "Tesla"

But both objects share the same start and info methods (memory efficient).


Constructor method — initialize objects

The constructor runs automatically when new is used. Use it to initialize properties.

Example:

class Person {
  constructor(name, age) {
    this.name = name;  // instance property
    this.age = age;    // instance property
  }

  greet() {
    console.log(`Hi, I'm \({this.name} and I'm \){this.age} years old.`);
  }
}

const p = new Person("Riya", 21);
p.greet(); // Hi, I'm Riya and I'm 21 years old.

If you omit properties when creating an instance, they become undefined — so constructors are good for setting defaults.


Methods inside a class

Methods are functions that describe what an object can do.

class Student {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this.scores = [];
  }

  addScore(score) {
    this.scores.push(score);
  }

  averageScore() {
    if (this.scores.length === 0) return 0;
    const total = this.scores.reduce((a, b) => a + b, 0);
    return total / this.scores.length;
  }

  printDetails() {
    console.log(`\({this.name} (\){this.age} years) — Avg: ${this.averageScore()}`);
  }
}
  • addScore, averageScore, and printDetails are behaviors related to each Student.

  • Methods use this to access instance properties.


Basic idea of encapsulation (beginner level)

Encapsulation means bundling data and the operations that modify that data inside an object, and restricting direct access to some of the object’s components. That helps prevent accidental changes.

Simple convention (beginner-friendly)

A common beginner approach is using an underscore prefix to indicate "private" properties:

class BankAccount {
  constructor(owner, balance = 0) {
    this.owner = owner;
    this._balance = balance; // "_" indicates "private by convention"
  }

  deposit(amount) {
    if (amount > 0) this._balance += amount;
  }

  withdraw(amount) {
    if (amount > 0 && amount <= this._balance) {
      this._balance -= amount;
      return amount;
    }
    console.log("Insufficient funds!");
    return 0;
  }

  getBalance() {
    return this._balance;
  }
}

const acct = new BankAccount("Asha", 500);
acct.withdraw(100);
console.log(acct.getBalance()); // 400
// acct._balance = 100000  // technically possible, but discouraged

This approach relies on developer discipline (it’s a convention).

Private fields (modern JS — brief mention)

JavaScript supports private class fields using #. This truly hides the data from outside access:

class SecretBox {
  #secret; // private field
  constructor(secret) {
    this.#secret = secret;
  }
  reveal() {
    return this.#secret;
  }
}

const box = new SecretBox("gold");
console.log(box.reveal()); // gold
// console.log(box.#secret); // SyntaxError — truly private

Note: # syntax is modern and widely supported in current environments, but beginners can start with the underscore convention and the public API (getters/setters).


Small example set (Person, Student, Car)

Person

class Person {
  constructor(name) {
    this.name = name;
  }
  sayHello() {
    console.log(`Hello, I'm ${this.name}`);
  }
}

const alice = new Person("Alice");
alice.sayHello(); // Hello, I'm Alice

Student (reusability emphasis)

class Student {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this.courses = [];
  }

  enroll(course) {
    this.courses.push(course);
  }

  printDetails() {
    console.log(`\({this.name}, Age: \){this.age}. Courses: ${this.courses.join(", ")}`);
  }
}

const s1 = new Student("Raj", 20);
s1.enroll("JavaScript");
s1.enroll("HTML");
s1.printDetails(); // Raj, Age: 20. Courses: JavaScript, HTML

Car (blueprint → object)

class Car {
  constructor(make, model) {
    this.make = make;
    this.model = model;
    this.isRunning = false;
  }

  start() {
    this.isRunning = true;
    console.log(`\({this.make} \){this.model} started.`);
  }

  stop() {
    this.isRunning = false;
    console.log(`\({this.make} \){this.model} stopped.`);
  }
}

const carA = new Car("Honda", "Civic");
const carB = new Car("Ford", "Figo");

carA.start(); // Honda Civic started.
carB.start(); // Ford Figo started.

You define behavior once in the class; all instances (carA, carB) have that behavior.


Why OOP helps with reusability

  • Define a class once, create many instances with different data.

  • Add or fix a method in one place (the class) and all instances benefit.

  • Keeps related code (data + behavior) close together — easier to reason about.

Example: if you need to change how printDetails() formats output for all students, change it in the class — all student objects will reflect the change.


Assignment Idea (with solution)

Assignment

  1. Create a class called Student.

  2. Add properties name and age.

  3. Add a method printDetails() that logs the student’s name and age.

  4. Create 3 student objects and call printDetails() on each.

Solution

class Student {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  printDetails() {
    console.log(`Student: \({this.name}, Age: \){this.age}`);
  }
}

const st1 = new Student("Aman", 19);
const st2 = new Student("Priya", 21);
const st3 = new Student("Sahil", 22);

st1.printDetails(); // Student: Aman, Age: 19
st2.printDetails(); // Student: Priya, Age: 21
st3.printDetails(); // Student: Sahil, Age: 22

Try this extension (practice):

  • Add a courses property (array) and a method enroll(course) that pushes to courses.

  • Print the student’s courses in printDetails().