JavaScript supports Object-Oriented Programming (OOP) through objects, prototypes, and classes. ES6 introduced a cleaner class syntax, making OOP easier to understand and write. In this blog, I'll explain the four pillars of OOP and some useful class features in JavaScript.

1. Inheritance

Inheritance allows one class to reuse the properties and methods of another class.

Example:

class Animal {
eat() {
console.log("Eating");
}
}

class Dog extends Animal {}

const dog = new Dog();

dog.eat();

Since Dog extends Animal, it inherits the eat() method.

This helps us avoid writing the same code again.


2. Polymorphism

Polymorphism means one method can behave differently depending on the object that calls it.

In JavaScript, this is usually achieved by overriding a parent class method in a child class.

Example

class Animal {
speak() {
console.log("Animal makes a sound");
}
}

class Dog extends Animal {
speak() {
console.log("Dog barks");
}
}

class Cat extends Animal {
speak() {
console.log("Cat meows");
}
}

const dog = new Dog();
const cat = new Cat();

dog.speak();
cat.speak();

Output:

Dog barks
Cat meows

Here, all classes have the same speak() method, but each class provides its own implementation. This is called polymorphism.


3. Abstraction

Abstraction means showing only the necessary functionality and hiding the internal implementation.

For example, when we start a car, we only press the start button. We don't need to know how the engine, fuel system, and battery work internally.

JavaScript allows us to achieve abstraction using classes and private methods or private fields.

Example

class Car {
start() {
this.#checkFuel();
console.log("Car Started");
}

#checkFuel() {
    console.log("Checking Fuel...");
}

Enter fullscreen mode Exit fullscreen mode

}

const car = new Car();

car.start();

Output:

Checking Fuel...
Car Started

Enter fullscreen mode Exit fullscreen mode

The user only calls start(). The internal method #checkFuel() remains hidden from outside the class. This makes the code easier to use and protects the internal implementation.

4. Static Members

Normally, we create an object before calling a method.

const obj = new Person();

But some methods belong to the class itself, not to its objects.

These are called static methods.

Example:

class MathUtil {
static add(a, b) {
return a + b;
}
}

console.log(MathUtil.add(5, 3));

Notice that we didn't create an object.


5. Getters and Setters

Getters and setters allow us to control how properties are read and updated.

Example:

class Person {
constructor(name) {
this._name = name;
}
get name() {
return this._name;
}

set name(value) {
    if (value.length >= 3) {
        this._name = value;
    }
}

Enter fullscreen mode Exit fullscreen mode

}

This helps us validate data before updating it.


6. Encapsulation Using Closures

Encapsulation means hiding data so it cannot be changed directly.

Example:

function BankAccount() {
let balance = 1000;
return {
deposit(amount) {
balance += amount;
},

    getBalance() {
        return balance;
    }
};

Enter fullscreen mode Exit fullscreen mode

}
const account = BankAccount();
account.deposit(500);
console.log(account.getBalance());

The variable balance cannot be accessed directly from outside the function.


7. Private Fields (#field)

Modern JavaScript provides another way to hide data using private fields.

Example:

class BankAccount {
#balance = 1000;
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount();
account.deposit(500);
console.log(account.getBalance());
Trying to access:
account.#balance;

will cause an error because private fields can only be accessed inside the class.

Conclusion

Now I know that JavaScript's OOP system is built on prototypes, and classes simply make the syntax cleaner and easier to read. Understanding these concepts gives a strong foundation for writing better JavaScript code.