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

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}`;
}
}
constructoris a special method called when you create an object withnew.thisrefers 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, andprintDetailsare behaviors related to eachStudent.Methods use
thisto 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
Create a class called
Student.Add properties
nameandage.Add a method
printDetails()that logs the student’s name and age.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
coursesproperty (array) and a methodenroll(course)that pushes tocourses.Print the student’s courses in
printDetails().



