# A Beginner's Guide to Object-Oriented Programming in JavaScript

JavaScript was often considered a language primarily suited for scripting interactions on web pages—dominating the front-end development world with its event-driven and functional programming style. However, as JavaScript evolved, so did its programming paradigms. With the introduction of ES6 and beyond, JavaScript now offers cleaner syntax and structures that make it more straightforward to write Object-Oriented Programming (OOP) code. Today, JavaScript comfortably supports classes, inheritance, encapsulation, and more.

In this post, we’ll dive deep into the world of OOP in JavaScript. We’ll start with the basics of objects, walk through prototype-based inheritance, and then explore modern class syntax, constructors, and various OOP principles such as encapsulation, inheritance, and polymorphism.

## **What is object-oriented programming?**

Object-Oriented Programming (OOP) is a programming paradigm centered around the concept of “objects” rather than functions and logic alone. Objects represent entities that combine both data (properties) and behavior (methods) into a single, self-contained unit. OOP aims to make code more modular, extensible, and easier to maintain by modeling real-world concepts directly within the codebase.

Core concepts of OOP include:

1. **Encapsulation:** grouping data and functions together into logical units—objects—that keep internal data safe and accessible only through well-defined interfaces.
    
2. **Abstraction:** hiding complex internal details and showing only the necessary features of an object.
    
3. **Inheritance:** Deriving new objects (classes) from existing ones, allowing for code reuse and hierarchical classifications.
    
4. **Polymorphism:** Providing a single interface for different underlying data types, allowing objects to be treated uniformly while still preserving their specialized behavior.
    

## **OOP in JavaScript: A Brief Historical Context**

Before ES6 (ECMAScript 2015), JavaScript didn’t have a built-in class keyword. Instead, JavaScript relied on a prototype-based inheritance model. Every object in JavaScript is linked to a prototype object from which it can inherit properties. While incredibly powerful and flexible, this prototype-based model often confused developers coming from class-based languages like Java, C#, or C++.

The introduction of the `class` syntax in ES6 brought a more familiar and “classical” feel to OOP in JavaScript, even though under the hood it still uses prototypes. The new syntax made JavaScript more approachable for developers used to classical inheritance while still retaining JavaScript’s dynamic and flexible nature.

## **Objects: The Building Blocks**

In JavaScript, almost everything is an object, or can behave like one. An object is simply a collection of key-value pairs. Here’s a simple example:

```plaintext
const person = {
  name: 'Alice',
  age: 25,
  greet: function() {
    console.log(`Hello, my name is ${this.name}!`);
  }
};

person.greet(); // "Hello, my name is Alice!"
```

This `person` object encapsulates data (name, age) and a behavior (greet). Notice how `this` refers to the current object instance, making it straightforward to access its own properties.

## **Prototype-Based Inheritance: The Traditional Approach**

Before `class` syntax, inheritance in JavaScript was traditionally managed through prototypes. When you access a property on an object, JavaScript will look it up in the object itself, and if it doesn’t find it, it will look for it in the object’s prototype.

For example:

```plaintext
function Person(name, age) {
  this.name = name;
  this.age = age;
}

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

const alice = new Person('Alice', 25);
alice.greet(); // "Hello, my name is Alice!"
```

Here, `Person` acts like a constructor function. Instances created by `new Person()` inherit the⁣`greet` method from `Person.prototype`. If we create `const bob = new Person('Bob', 30);`, bob also has `greet()` even though `greet` isn’t defined directly on `bob`.

To extend this idea, we can create another constructor that inherits from `Person`:

```plaintext
function Employee(name, age, position) {
  Person.call(this, name, age);
  this.position = position;
}

Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;

Employee.prototype.work = function() {
  console.log(`${this.name} is working as a ${this.position}.`);
};

const charlie = new Employee('Charlie', 28, 'Engineer');
charlie.greet();  // Inherited from Person
charlie.work();   // Defined in Employee
```

Although effective, this approach is verbose and less intuitive compared to class-based languages.

## **Modern Class Syntax**

With ES6, JavaScript introduced the `class` keyword, which provides a cleaner and more familiar syntax for creating objects and handling inheritance. Under the hood, it’s still using prototypes, but now we have a more elegant approach.

## **Defining Classes**

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

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

const alice = new Person('Alice', 25);
alice.greet(); // "Hello, my name is Alice!"
```

In this example:

* `class Person` defines a new class.
    
* The `constructor` method is a special method that runs automatically when a new instance is created.
    
* Methods defined inside the class body (`greet()`) are automatically placed on the `Person.prototype`, ensuring all instances share that method.
    

## **Inheritance with Classes**

We can use the `extends` keyword to create subclasses:

```plaintext
class Employee extends Person {
  constructor(name, age, position) {
    super(name, age); // calls the Person constructor
    this.position = position;
  }

  work() {
    console.log(`${this.name} is working as a ${this.position}.`);
  }
}

const charlie = new Employee('Charlie', 28, 'Engineer');
charlie.greet(); // Inherited from Person: "Hello, my name is Charlie!"
charlie.work();  // "Charlie is working as a Engineer."
```

* `class Employee extends Person` tells JavaScript that `Employee` is a subclass of `Person`.
    
* We call `super(name, age)` inside the `Employee` constructor to initialize the `Person` part of our object. `super` refers to the parent class.
    
* `Employee` now inherits all properties and methods of `Person`, plus it adds its own.
    

## **OOP Principles in Action**

## **Encapsulation**

Encapsulation means bundling data and methods that operate on that data within one unit. In traditional OOP languages, encapsulation often involves controlling access to properties using public, protected, and private keywords. JavaScript classes don’t have built-in access modifiers in the same way, but with modern JavaScript (ES2022 and beyond), we have private class fields:

```plaintext
class BankAccount {
  #balance; // private field

  constructor(initialBalance) {
    this.#balance = initialBalance;
  }

  deposit(amount) {
    this.#balance += amount;
  }

  withdraw(amount) {
    if (amount <= this.#balance) {
      this.#balance -= amount;
    } else {
      console.log('Insufficient funds.');
    }
  }

  getBalance() {
    return this.#balance;
  }
}

const account = new BankAccount(1000);
account.deposit(500);
account.withdraw(200);
console.log(account.getBalance()); // 1300
console.log(account.#balance); // SyntaxError: Private field '#balance' must be declared in an enclosing class
```

Private fields (`#balance`) cannot be accessed or modified directly outside the class. This feature is relatively new and provides true encapsulation.

## **Inheritance**

We demonstrated inheritance above. It lets one class build upon another, promoting code reuse and logical structure. Inheritance also encourages hierarchical thinking — `Employee` is a `Person` with additional traits.

## **Polymorphism**

Polymorphism means the same method name can behave differently depending on the object calling it. For instance, if you have a `describe()` method in a parent class and you override it in a subclass, each object’s `describe()` method behaves according to its own class definition:

```plaintext
class Shape {
  describe() {
    console.log('This is a generic shape.');
  }
}

class Circle extends Shape {
  describe() {
    console.log('This is a circle.');
  }
}

class Square extends Shape {
  describe() {
    console.log('This is a square.');
  }
}

const shapes = [new Shape(), new Circle(), new Square()];
shapes.forEach(shape => shape.describe());
// "This is a generic shape."
// "This is a circle."
// "This is a square."
```

Despite calling `describe()` on each shape, the behavior differs depending on the instance’s class, showing polymorphic behavior.

## **When to Use OOP in JavaScript**

OOP is not always the best approach for every problem. JavaScript is a multi-paradigm language, meaning you can also use functional programming (FP), procedural, or a mix. OOP shines when:

* You have complex data structures and behaviors that naturally fit into entities (objects).
    
* You want to model real-world objects or hierarchical relationships.
    
* Your codebase can benefit from encapsulation, modularity, and reusability.
    

For smaller scripts or highly functional tasks, FP might be simpler and more direct. Choose the paradigm that makes your code more understandable and maintainable.

## **Conclusion**

JavaScript’s flexibility enables developers to adopt various programming styles. With modern syntax and language features, writing object-oriented JavaScript code has never been more intuitive. Classes, constructors, inheritance, private fields, and polymorphism allow you to structure code in a way that’s familiar to those coming from classical OOP languages while still taking advantage of JavaScript’s unique strengths.

Whether you’re building small web components or architecting large-scale applications, understanding OOP principles in JavaScript gives you another powerful tool to shape your code into maintainable, modular, and robust software.

**Further Reading & Resources:**

* [MDN Web Docs on Classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)
    
* [MDN Web Docs on Inheritance and the prototype chain](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)
    
* [You Don’t Know JS: this & Object Prototypes](https://github.com/getify/You-Dont-Know-JS/tree/2nd-ed/this%20%26%20object%20prototypes)
    

Embrace the OOP paradigm when it fits your project’s needs, and enjoy the clarity and structure it can bring to your JavaScript code.

## **📌 Stay Updated**

Follow me for more design tips and tools! ✨

🐙 [GitHub](https://github.com/developerchandan): Follow me for more web development resources.  
🔗 [LinkedIn](https://www.linkedin.com/in/developerchandan/): Connect with me for tips and tricks in coding and productivity.  
✍️ [Medium](https://medium.com/@developerchandan): Follow me for in-depth articles on web development and productivity.
