The Complete Guide to OOP In Javascript
Tags: javascript, webdev, programming, development
Published: 2023-06-02 07:17:45

We know that Javascript is a multi-paradigm language. That is, it supports functional as well as object oriented programming. 

In this blog, I am explaining the concept of OOP in Javascript in detail. 

## What is Object Oriented Programming

- Object Oriented Programming is a programming paradigm based on the concept of objects.

- Objects are used to model (describe) the real-world or abstract features.

- Objects contain data (properties) and behaviour (methods). By using objects we can pack data and corresponding behaviour in one block.

- OOP was developed with the goal of organizing code, to make more flexible and easier to maintain (avoid spaghetti code).

There are six very important terminologies related to OOP:

1] **Classes**

- Class is a blueprint or templates to build new objects.
- Classes are non-tangible logical entity, that is, they don't occupy memory.
- A class can exist without its object but converse is not true.

2] **Objects**

- Objects are instances of class.
- They are real world entities, ie they occupy memory.

3] **Data Abstraction**

- Data abstraction means hiding the unnecessary details and only 
  showing the useful data to the user.

4] **Data Encapsulation**

- Encapsulation means wrapping up of properties and behaviours in a single unit.
- Encapsulation is achieved by making all the properties and some methods private in a class.
- Encapsulation leads to data hiding.
- Getters are used to accept values and setters are used to change the class state, hence restricting the outsiders to manipulate the class state.
- Encapsulation promotes loose coupling, ie, other sections of the code can be changed or refactored without affecting another parts.
- Encapsulation helps in maintaining security and avoiding accidental bugs, because the properties/fields are hidden from the outer world. 
- In Javascript, encapsulation can be achieved by using the ES6 classes as well as function closures.


5] **Polymorphism**

- Polymorphism means the ability of a method to act in more the one way, depending upon the situation.
- A child class can override the methods present in its parent class.
- It promotes the DRY (Don't Repeat Yourself) principle.


6] **Inheritance**

- It means the ability of an object to inherit properties and behaviours from its parent object.
- Inheritance promotes reusability and maintainability.

<hr>

Now we have learnt the important terminologies of OOP. Let us now learn how actually OOP is implemented in Javascript.

OOP in Javascript is completely different from other traditional object oriented programming languages like C++ and Java.  

- Everything in Javascript is either a primitive or an object. 

**In Javascript, Object Oriented Programming is implemented using Prototypes and Prototypal Inheritence**

**Prototypes**
Prototypes are the mechanism by which Javascript objects inherit features from one another.

**Prototypal Inheritance**
Before going to the definition of prototypal inheritance let me ask you a question. 

You write the following code in console.
<kbd>![Prototypal Inheritance Demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9sw4d84mhcokdx4jl15y.png)</kbd>

Here, you have made only one property in the Student object, but you can see in the following image that there are other properties and methods already available in the Student object along with the **name** property.

<kbd>
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h4cf2nifdetugmhof98z.png)

</kbd>

So how is that possible? It is possible because of prototypal inheritance.

Prototype inheritance in javascript is the linking of prototypes of a parent object to a child object to share and utilize the properties of a parent class using a child class.

Prototypes are hidden objects that are used to share the properties and methods of a parent class to child classes.

The prototype contains methods that are accessible to all objects linked to that prototype.

## Classical OOP vs OOP in JS


<kbd>
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gn11au38vo2gdupq2qrz.png)
</kbd>

- In classical OOP, the child class have all the public properties and methods of its parent class. That is, flow of data is from parent to child.
- In Javascript, the child object delegates or entrust the methods and properties to its prototype object. Thai is, flow of data is from child to parent.

<hr>

## How do we actually create prototypes? And how do we link objects to prototypes? How can we create new objects, without having classes?

Prototypes can be created using three ways:

1. Constructor functions
2. ES6 classes
3. Object.create() method

Now, I will explain each way in detail :)

## 1. Constructor Functions

When a regular function is called using the **new** keyword, then the function acts as a constructor fuction and returns a brand new object, whose properties and methods can be set by passing parameters and attaching those values to the **this** keyword.

```javascript

let Student = function (firstName,lastName){
    this.firstName = firstName
    this.lastName = lastName
}

let rudra = new Student("Rudra","Pratap")

console.log(rudra) // Student { firstName: 'Rudra', lastName: 'Pratap' }
console.log(rudra.firstName) // Rudra

```

Now, we can use this **Student** constructor function to declare as many other objects.

```javascript

let rudra = new Student("Rudra","Pratap")
console.log(rudra.firstName) // Rudra

let tim = new Student("Tim","Cook")
console.log(tim.lastName) // Cook

let zuck = new Student("Mark","Zuck")
console.log(zuck.firstName) // Zuck

```
<br>
Every constructor function has a property called prototype. And that prototype property contains at least two more properties : the definition of the constructor function and a **\_\_proto\_\_** object.

<kbd>
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nuhojg7bfdlkb4uzf5j0.png)
</kbd> 

Every object of a constructor function have access to the properties and methods present in the prototype of its parent constructor function.

<br>
**Note:** Anonymous functions can't be made constructor functions.

Regular functions can be made constructors.
<kbd>
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eu69hz5092pyk79fjt0y.png)
</kbd>
Anonymous functions can't be made constructors.
<kbd>
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yfaypdvxi53fncejq30a.png)
</kbd>


<br>
Let suppose you want a method **introduceMyself()**, that can be called on each object, how can we do it?

**First solution [Naive]**

```javascript

let Student = function (firstName,lastName){
    this.firstName = firstName
    this.lastName = lastName
    
    this.introduceMyself = function(){
        console.log(`Hello, my name is ${this.firstName} ${this.lastName}.`)
    }
}

let rudra = new Student("Rudra","Pratap")
rudra.introduceMyself()

let tim = new Student("Tim","Cook") // Hello, my name is Rudra Pratap.
tim.introduceMyself() // Hello, my name is Tim Cook.


```
This will work as we wanted, but it is very inefficient. Suppose you are working on a large project having thousands of objects constructed by the **Student** constructor function, each of the object will have this **introduceMyself()** methods, as a result a large amount of memory will be wasted just to store a method which is common to all objects.

**Second Solution [Optimized]**

A efficient solution would be adding the **introduceMyself()** method in the constructor function's prototype.

```javascript

let Student = function (firstName,lastName){
    this.firstName = firstName
    this.lastName = lastName
}

Student.prototype.introduceMyself = function(){
     console.log(`Hello, my name is ${this.firstName} ${this.lastName}.`)
}

``` 
As we already know that the object has access to the prototype of its parent constructor function through the **\_\_proto\_\_** object.

Whenever we try to access a method or a property in an object by using a dot or bracket notation, that method/property is first searched inside the object, if it is found there then it is used, and if it is not found there, the Javascript engine will look into its parent object (here constructor function) prototype and tries to find out that method/property.

```javascript

let rp = new Student("Rudra","Pratap")
rp.introduceMyself() // Hello, my name is Rudra Pratap.

```
<br>
Hence, we can make many objects of a constructor function and we have to only define the methods once, inside its prototype. All its child object will automatically inherit those methods.

```javascript

let Student = function (firstName,lastName){
    this.firstName = firstName
    this.lastName = lastName
}

Student.prototype.introduceMyself = function(){
     console.log(`Hello, my name is ${this.firstName} ${this.lastName}.`)
}

let rp = new Student("Rudra","Pratap")
rp.introduceMyself() // Hello, my name is Rudra Pratap.

let tim = new Student("Tim","Cook")
tim.introduceMyself() // Hello, my name is Tim Cook.

let zuck = new Student("Mark","Zuck")
zuck.introduceMyself() // Hello, my name is Mark Zuck.

```



<hr>
## 2. ES6 Classes

- ES6 classes are just syntactic sugar over constructor functions.
- They are not like the classes found in traditional OOP languages like Java and C++.
- They are just the transformed form of constructor functions, under the hood they use the concept of prototypes and prototypal inheritance.
- Classes were added in Javascript in 2015 so that programmers from other backgrounds like Java and C++, can write object oriented code more easily.
- Using classes, we don’t have to manually mess with the prototype property.

<br>
Classes can be defined by two types: class declaration and class expression.

```javascript

let Person = class { ... } // class expression

class Person{ // class declaration
	....
}

```

A class must have a constructor method.
**Constructor Methods**

- The constructor method is a special method of a class for creating and initializing an object instance of that class.
- A class can't have more than one constructor method.
- Classes are not hoisted.


<br>
**Class Declaration : constructor functions vs classes**

1. using constructor function

```javascript

let Student = function (firstName,lastName){
    this.firstName = firstName
    this.lastName = lastName
}

```
2. using class
```javascript

class Student {
    constructor(firstName,lastName){
        this.firstName = firstName
        this.lastName = lastName
    }
}

```


<br>
**Defining methods : constructor functions vs classes**

1. In constructor function

```javascript

let Student = function (firstName,lastName){
    this.firstName = firstName
    this.lastName = lastName
}

Student.prototype.introduceMyself = function(){
     console.log(`Hello, my name is ${this.firstName} ${this.lastName}.`)
}


```

2. In class 

```javascript


class Student {
    constructor(firstName,lastName){
        this.firstName = firstName
        this.lastName = lastName
    }
    
    introduceMyself(){
     console.log(`Hello, my name is ${this.firstName} ${this.lastName}.`)
    }
    
}


```

Now we know that there is slight difference in syntax of constructor function and classes, but the way to instantiate them is exactly the same for each of them.

```javascript

let rp = new Student("Rudra","Pratap")
rp.introduceMyself() // Hello, my name is Rudra Pratap.

```




<hr>
## 2. Object.create() 
As I have already wrote that OOP in Javascript can be achieved by Constructor Functions, ES6 classes and Object.create(), it's time to discuss the last one.

Object.create() is used to manually set the prototype of an object to any other object that we want.

```javascript

let Person = {
    greet:function(){
        console.log(`Hi ${this.name}`)
    }
}

let rp = Object.create(Person)
rp.name="Rudra"

rp.greet() // Hi Rudra
console.log(rp.__proto__ === Person) // true
console.log(rp.__proto__.__proto__ === Object.prototype) // true
console.log(rp.__proto__.__proto__.__proto__) // null


```

```javascript

let Person = {
    greet:function(){
        console.log(`Hi ${this.name}`)
    }
}

let Teacher = Object.create(Person) 
Teacher.task = function(){
    console.log("I am teaching")
}

let MathTeacher = Object.create(Teacher)
MathTeacher.name = "Rudra"

MathTeacher.greet() // Hi Rudra
MathTeacher.task()  // I am teaching

console.log( MathTeacher.__proto__ === Teacher ) // true
console.log( MathTeacher.__proto__.__proto__ === Person ) // true
console.log( MathTeacher.__proto__.__proto__.__proto__ === Object.prototype ) // true
console.log( MathTeacher.__proto__.__proto__.__proto__.__proto__ ) // null

```


Object.create() is the least common way to implement prototypal inheritance, but if it is need then we can do more programmatically, like

```javascript

let Person = {
    init:function(firstName,lastName){
        this.firstName = firstName
        this.lastName = lastName
    },
    greet: function(){
        console.log(`Hi ${this.firstName} ${this.lastName}`)
    }
}

let rudra = Object.create(Person)
rudra.init("Rudra","Pratap") // it is not a constructor
rudra.greet() // Hi Rudra Pratap

```

**Puzzle**
Find the output of the following snippet

```javascript

var Employee = {
  company: 'xyz'
}
var emp1 = Object.create(Employee);
delete emp1.company
console.log(emp1.company);

```
Solution:
The answer is ‘xyz’, because there is no property company in emp1 object, but in its prototype which is Employee.

If you want to delete the property company, then

```javascript

var Employee = {
  company: 'xyz'
}
var emp1 = Object.create(Employee);
delete emp1.__proto__.company
console.log(emp1.company) // undefined
console.log(Employee) // {}

```