DEV Community

Bhupendra
Bhupendra

Posted on

JavaScript OOP Explained: From Prototypes to Classes and the 4 Pillars of Object-Oriented Programming 🚀

Object-Oriented Programming (OOP) in JavaScript can be a powerful way to structure and simplify your code. JavaScript’s unique OOP model combines the best of prototype-based inheritance with modern class syntax. Whether you’re a beginner or an experienced developer looking to refresh, this guide will walk you through everything from prototypes to classes and the four pillars of OOP. Let's dive in! 🌊


1. JavaScript Prototypes: The Foundation of OOP 🌱

In JavaScript, prototypes are the underlying mechanism for inheritance. Instead of the traditional class-based model seen in languages like Java or Python, JavaScript uses prototypes to share properties and methods across objects.

🔹 How Prototypes Work

Every JavaScript object has a hidden property called [[Prototype]] that links to another object. When you try to access a property or method on an object, JavaScript will look up the prototype chain until it finds the property.

Here's an example to illustrate:

// Creating a simple object
const animal = {
  speak() {
    console.log("Animal sound!");
  }
};

// Using Object.create to set a prototype
const dog = Object.create(animal);
dog.speak();  // Output: Animal sound!
Enter fullscreen mode Exit fullscreen mode

In this example, dog inherits the speak method from animal. JavaScript looks up the speak method in the prototype chain until it finds it in animal.

🔹 Prototype Chain

The prototype chain allows JavaScript objects to inherit properties and methods from other objects. Visualize it as a chain of objects linked together, with each object having access to the properties of the one before it.


2. Functional Constructors: The First Step to Object Creation 🛠️

Before ES6 classes, JavaScript developers used functional constructors to create objects. A functional constructor is simply a regular function that initializes a new object using the new keyword.

🔹 Creating Objects with Functional Constructors

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.greet = function() {
    console.log(`Hello, my name is ${this.name}.`);
  };
}

const john = new Person("John", 30);
john.greet();  // Output: Hello, my name is John.
Enter fullscreen mode Exit fullscreen mode

Here, Person is a constructor function that creates new Person instances when called with new.

🔹 Adding Methods to the Prototype

To save memory, we can add methods to the function’s prototype, rather than redefining them for each instance.

Person.prototype.sayGoodbye = function() {
  console.log(`${this.name} says goodbye!`);
};

john.sayGoodbye();  // Output: John says goodbye!
Enter fullscreen mode Exit fullscreen mode

By attaching sayGoodbye to the prototype, all Person instances share a single version of the method.


3. ES6 Classes and Objects: The Modern Approach 🧑‍💻

In ES6, JavaScript introduced a more structured way to work with objects and inheritance through the class keyword. Classes are essentially syntactic sugar over JavaScript’s prototypal inheritance, making the code easier to read and organize.

🔹 Defining Classes

Here’s how we can rewrite our Person constructor using classes:

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

  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

const jane = new Person("Jane", 25);
jane.greet();  // Output: Hello, my name is Jane.
Enter fullscreen mode Exit fullscreen mode

The class syntax includes a constructor method for initializing objects, and methods can be defined directly inside the class.

🔹 Inheritance with Classes

With classes, inheritance becomes simpler using the extends keyword:

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

  study() {
    console.log(`${this.name} is studying.`);
  }
}

const sam = new Student("Sam", 20, "A");
sam.study();      // Output: Sam is studying.
sam.greet();      // Output: Hello, my name is Sam.
Enter fullscreen mode Exit fullscreen mode

Using extends and super, we can create child classes that inherit from parent classes, providing even more flexibility in structuring our code.


4. The 4 Pillars of OOP in JavaScript 💡

Object-oriented programming is built around four key concepts: encapsulation, abstraction, inheritance, and polymorphism. Let’s see how these pillars apply to JavaScript.

🔹 Encapsulation: Bundling Data and Methods Together

Encapsulation is the bundling of data (properties) and methods (functions) that operate on that data within one object. In JavaScript, we achieve encapsulation by using closures or private fields (introduced in ES2022).

class Counter {
  #count = 0; // Private field

  increment() {
    this.#count++;
    console.log(this.#count);
  }
}

const counter = new Counter();
counter.increment();  // Output: 1
// counter.#count;    // Error: Private field
Enter fullscreen mode Exit fullscreen mode

🔹 Abstraction: Hiding Complexity

Abstraction lets us hide complex implementation details and expose only what’s necessary. JavaScript’s class and modules help us achieve this by providing controlled access.

class Car {
  startEngine() {
    console.log("Engine started.");
  }

  // Private method
  #prepareEngine() {
    console.log("Preparing engine...");
  }
}
Enter fullscreen mode Exit fullscreen mode

🔹 Inheritance: Creating Parent-Child Relationships

Inheritance allows a class (child) to inherit properties and methods from another class (parent). We saw this in action above with the Student class extending Person.

🔹 Polymorphism: Modifying Behavior in Subclasses

Polymorphism allows child classes to override methods inherited from parent classes. This enables multiple classes to be treated as instances of a common parent class while still allowing each subclass to have its unique behavior.

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

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

const dog = new Dog();
dog.speak();  // Output: Woof!
Enter fullscreen mode Exit fullscreen mode

Conclusion 🏁

Understanding JavaScript’s OOP model—from prototypes to classes and the four pillars of OOP—will enhance your ability to build robust, maintainable code. Embrace these concepts, and watch your code transform from functional to elegant! ✨

Ready to dive deeper? Experiment with these examples, build your own classes, and leverage the power of OOP in your JavaScript projects!


Top comments (0)