# Object-Oriented Programming in JavaScript: Classes, Objects & Encapsulation Explained

## Object-Oriented Programming in JavaScript: Classes, Objects & Encapsulation Explained

So far in this series, we've covered variables, control flow, operators, loops, arrays, array methods, functions, arrow functions, and object. Today we're stepping into a completely different way of *organizing* our code: **Object-Oriented Programming (OOP)**.

* * *

## 1\. What Does "Object-Oriented Programming" Actually Mean?

Until now, most of our code has been written as a series of steps - variables, loops, functions doing one task after another. That's called **procedural programming**.

**Object-Oriented Programming (OOP)** is a different style of writing code. Instead of thinking in "steps," you think in terms of **things (objects)** that have:

*   **Properties** - data that describes the thing (e.g., a car's color, brand, speed)
    
*   **Behaviors** - actions the thing can perform (e.g., a car can `start()` or `stop()`)
    

So instead of writing loose variables and functions floating around, you group related data and behavior together into a single, reusable unit called an **object**.

> **In short:** OOP is a way of modeling real-world things in code, by bundling data and the functions that work on that data into one package.

* * *

## 2\. A Real-World Analogy: Blueprint -> Objects

Here's the analogy that made OOP click for me.

Imagine a **car manufacturer**. Before they build a single car, they design a **blueprint** - a detailed plan that says: every car will have a `brand`, a `color`, a `topSpeed`, and it will be able to `start()` and `stop()`.

The blueprint itself is **not a car**. You can't drive a blueprint. But using that one blueprint, the factory can produce **many actual cars** - a red Honda, a blue Toyota, a black Tesla - each one following the same design, but each with its own specific details.

![](https://cdn.hashnode.com/uploads/covers/69413d2ffd5a397514bc42f5/ab9ebbc7-5e60-43d3-9b46-867b287ed5a1.jpg align="center")

In JavaScript:

*   The **blueprint** is called a **class**
    
*   Each **actual car** built from that blueprint is called an **object** (or an **instance**)
    

* * *

## 3\. What is a Class in JavaScript?

A **class** is a template (or blueprint) that defines what properties and methods every object created from it should have.

Here's the most basic class you can write:

```javascript
class Car {
  // properties and methods go here
}
```

That's it - this is a class named `Car`. Right now it's empty, like a blueprint with no details filled in. Let's fix that.

* * *

## 4\. Creating Objects Using Classes

Once you have a class, you can create as many objects from it as you want using the `new` keyword. This process is called **instantiation** - you're creating an **instance** of the class.

```javascript
class Car {}

const car1 = new Car();
const car2 = new Car();

console.log(car1); // Car {}
console.log(car2); // Car {}
console.log(car1 === car2); // false
```

Even though `car1` and `car2` came from the exact same blueprint, they are **two separate, independent objects** - just like two cars rolling off the same factory line are still different physical cars.

* * *

## 5\. The Constructor Method

An empty blueprint isn't very useful. We want every car to come with its own `brand` and `color` right from the moment it's created. That's exactly what the **constructor** is for.

The `constructor` is a special method inside a class that runs **automatically** whenever a new object is created with `new`.

```javascript
class Car {
  constructor(brand, color) {
    this.brand = brand;
    this.color = color;
  }
}

const car1 = new Car("Honda", "Red");
const car2 = new Car("Toyota", "Blue");

console.log(car1.brand); // "Honda"
console.log(car2.color); // "Blue"
```

Let's break this down:

| Part | What it means |
| --- | --- |
| `constructor(brand, color)` | Runs once, automatically, when `new Car(...)` is called |
| `this.brand = brand` | `this` refers to *the object currently being created* |
| `new Car("Honda", "Red")` | Creates a new object and passes values into the constructor |

> Think of `this` as a placeholder that means "whichever object I'm building right now."

* * *

## 6\. Methods Inside a Class

A class can also hold behaviors, written as regular functions inside the class - these are called methods.

```javascript
class Car {
  constructor(brand, color) {
    this.brand = brand;
    this.color = color;
  }

  start() {
    console.log(`${this.brand} car has started.`);
  }

  honk() {
    console.log(`${this.brand} says: Beep beep!`);
  }
}

const myCar = new Car("Honda", "Red");
myCar.start(); // "Honda car has started."
myCar.honk();  // "Honda says: Beep beep!"
```

Notice we didn't need to write function before start() or honk() - inside a class, that's just the syntax for defining a method. Also notice this.brand is used again - inside a method, this still refers to the object the method was called on (myCar in this case).

* * *

## 7\. The Basic Idea of Encapsulation

**Encapsulation** is a fancy word for a simple idea: **bundling data and the methods that work on that data together, inside one object**, instead of scattering them around as separate variables and functions.

Compare these two approaches:

**Without OOP (scattered):**

```javascript
let carBrand = "Honda";
let carColor = "Red";

function startCar(brand) {
  console.log(`${brand} car has started.`);
}

startCar(carBrand); // Honda car has started.
```

Here, `carBrand`, `carColor`, and `startCar` are all separate - nothing ties them together. If you had 10 cars, you'd need 10 sets of loose variables, which gets messy fast.

**With OOP (encapsulated):**

```javascript
class Car {
  constructor(brand, color) {
    this.brand = brand;
    this.color = color;
  }
  start() {
    console.log(`${this.brand} car has started.`);
  }
}

const myCar = new Car("Honda", "Red");
myCar.start(); // Honda car has started.
```

Now `brand`, `color`, and `start()` all live **inside** the `Car` object, neatly packaged together. `myCar` carries its own data *and* its own behavior wherever it goes.

> **Beginner scope note:** True encapsulation also involves *hiding* internal data (using private fields, getters/setters) so it can't be changed carelessly from outside. That's a slightly more advanced topic we'll save for a future article - for now, just focus on the core idea: **grouping related data and behavior together.**

* * *

## Class vs. Object - Quick Reference

![](https://cdn.hashnode.com/uploads/covers/69413d2ffd5a397514bc42f5/be3f471a-cad0-4b01-bb2c-96f45ac47050.jpg align="center")

| Concept | Analogy | JavaScript |
| --- | --- | --- |
| Class | The blueprint | `class Car { ... }` |
| Object / Instance | An actual car built from it | `const myCar = new Car(...)` |
| Constructor | The assembly step that fills in details | `constructor(brand, color) { ... }` |
| Method | Something the car can do | `start() { ... }` |
| Encapsulation | Everything about the car lives inside the car | data + methods bundled in one object |

* * *

## Try It Yourself (Console Practice)

Open your browser console or Node REPL and play with this:

```javascript
class Student {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  greet() {
    console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old.`);
  }
}

const s1 = new Student("Aarav", 21);
s1.greet(); // Hi, I'm Aarav and I'm 21 years old.
```

Try changing the name and age, adding a second student, and calling `greet()` on both. Notice how each object keeps track of its *own* data.

* * *

## Assignment

**Task:** Create a `Student` class that models a student's basic details.

**Requirements:**

1.  Create a class called `Student`
    
2.  Add properties `name` and `age` (set via the constructor)
    
3.  Add a method `printDetails()` that logs the student's details
    
4.  Create **at least 3** different `Student` objects and call the method on each
    

Try building this yourself before expanding the solution below!

<details> <summary><strong>Click to reveal the solution</strong></summary>

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

  printDetails() {
    console.log(`Name: ${this.name}, Age: ${this.age}`);
  }
}

const student1 = new Student("Aarav", 21);
const student2 = new Student("Diya", 19);
const student3 = new Student("Kabir", 23);

student1.printDetails(); // Name: Aarav, Age: 21
student2.printDetails(); // Name: Diya, Age: 19
student3.printDetails(); // Name: Kabir, Age: 23
```

**What's happening here:**

*   `Student` is the blueprint (class)
    
*   `student1`, `student2`, `student3` are three independent objects (instances)
    
*   Each one stores its *own* `name` and `age`
    
*   `printDetails()` is one shared method, but it works with whichever object called it - thanks to `this`
    

</details>

* * *

## Why This Matters: Code Reusability

Without classes, if you wanted 50 students, you'd be copy-pasting variables and functions 50 times. With a class, you write the blueprint **once**, and then create as many objects as you need - each one automatically getting the same structure and behavior, with its own unique data.

This is one of the biggest wins of OOP: **write once, reuse everywhere.**

* * *

## Key Takeaways

*   **OOP** organizes code around objects that bundle data + behavior together
    
*   A **class** is a blueprint; an **object** is a real instance built from that blueprint
    
*   The **constructor** runs automatically when you create an object with `new`, and sets up its initial data
    
*   **Methods** are functions that live inside a class and define what an object can *do*
    
*   **Encapsulation** (at a basic level) just means keeping related data and behavior bundled together in one object
    

* * *

## What's Next?

In the next article, we'll dig into the `this` **keyword** in JavaScript in more detail - how it behaves inside regular functions, arrow functions, and methods, since it's the glue that holds everything we learned today together. Stay tuned
