Prototypes in JS
Tags: javascript, webdev, programming
Published: 2021-10-24 16:55:29

## Motivation

Lately, I had been reading lots of articles for core concepts of Javascript. After reading a lot of them, I became overwhelmed with so much of knowledge all at once. Though I had been writing keypoints of each concept in docs but I realized it would be better for me to create detailed articles for the concepts that I have been learning.

Now let's start.

## Code Example
Let us consider a small program first

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

let john = new Person('John',24)
```
The above program has a constructor `Person` and creates its instance `john`. Let's log this in browser console

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jhkoyc1kn768qiy2nexk.png)
 
Here we have a `[[Prototype]]` property defined apart from the two properties. On opening it further we have `constructor` having the value of `Person` function and `[[Prototype]]` defined once again and further it has a list of properties. 
Seeing this we can conclude that there are a bunch of things happening behind as we define just a simple function. We need to understand this for more clarity. For that, let's first understand about Prototype.

## What is Prototype ?

According to MDN,
> Prototypes are the mechanism by which JavaScript objects inherit features from one another.

By understanding this, definitely there is something related to inheritance. Let's define it a bit more clearly.

**Prototype is an object that is available on every function that we define in JS code.**  
It consists of two things :-
- A `constructor` pointing back to the function on which `prototype` has been defined
- `__proto__`(dunder proto) object having the value of prototype of current function's prototype. It same as `[[Prototype]]` but we access it using `__proto__`. For this article let's use `__proto__` in place of `[[Prototype]]`

These two things would be very important to understand for this article. 

Let's use the constructor defined above for depicting the same. 

![Constructor and prototype](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tmvk5isceeo6uwslsjrh.png)
 

Now let's create two objects for the same.

```javascript
let john = new Person('John',24);
let james = new Person('James',20);
```
Logging `john` and `james` on the console gives us this
![Loggin john](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y9mg6eucssy9biifl165.png)
![Logging james](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dswm9sk2npdlm2b2qfjv.png)
 
Here we can see that both of them have the required set of properties as defined in `Person` and have a `__proto__` object defined. 
On opening it further, we can see that it has `Person` constructor and `__proto__` object defined. Both of these are same as that of the Person's prototype. Let's check if both are same through referential equality.

```javascript
console.log(Person.prototype === john.__proto__); // true
```
Therefore, we conclude that:- 
* A constructor's prototype is same as that of its instance. We can access constructor's prototype using `.prototype` while using `.__proto__` for that of its instance.
* All the instances share the same prototype object of the  constructor.

![Constructor with its instances and prototype](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bai44innsjc2swrl9e3d.png)
 

Now that we have understood this much, lets expand the prototype of `Person` now.
![EXpanding Person prototype](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p8vcupbge3amv4kkcbu1.png)
 
 
Here we can seen that `Person` has a prototype of `Object` which again has a prototype of `Object` until `null`. This concept of linking prototype object to the parent constructors prototypes all the way up until `null` is called as [Prototype chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain).

## Some observations on prototype properties

_**NOTE:** The examples used in this section are just for understanding purposes and not to be used for creating real world objects._

* **Object properties are accessed all the way up through the prototype chain** 

Let's redefine our constructor in a different way now and create an object for the same. 

```javascript
function Person(){}
Person.prototype.name = "John";
Person.prototype.age = 23;

let john = new Person();

console.log(john); // {}
console.log(john.name);  // 'John'
console.log(john.age);  // 23
``` 

Here we can see that we can access the properties of `john` which are not defined in the constructor but defined in its prototype. 
This has been achieved through Prototype chaining. All its parent prototypes are searched for the property until we encounter the required property to be accessed. This means that if we had not defined `name` and `age` in the prototype, all the prototypes of `john` have been searched recursively for the property which had not been defined even at the last object up in the prototype chain which would have given us `undefined`.

```javascript
function Person(){}
let john = new Person();

console.log(john); // {}
console.log(john.name);  // undefined
console.log(john.age);  // undefined
``` 

* **Setting an object property makes it a direct child even if it has already been defined in its prototype** 

```javascript
function Person(){}
Person.prototype.name = "John";

let john = new Person();
console.log(john.name);  // 'John'
john.name = 'Carl';
console.log(john.name);  // 'Carl'
delete john.name;
console.log(john.name);  // 'John'
delete john.name;
console.log(john.name);  // 'John'
delete john.__proto__.name;
console.log(john.name);  // undefined
``` 
Here we can see that on directly setting a property, it becomes a direct child of the object even though it exists in its prototype. Also deletion of property defined in prototype does not happen until we delete it after accessing the object within its prototype.

* **Updating a reference type property (through prototype methods) defined in prototype of a constructor modifies it for all its instances**

```javascript
function Person(){}
Person.prototype.friends = ['James','Jaden']
let john = new Person(),
    joe = new Person();

console.log(john.fields); // ['James','Jaden']
console.log(joe.fields); // ['James','Jaden']
john.friends.splice(1,0,'Jenny','Joseph');
console.log(john.friends); // ['James','Jenny','Joseph','Jaden'];
console.log(joe.friends); // ['James','Jenny','Joseph','Jaden'];
``` 
I hope the example is self-explanatory from the title itself. :D.

By the last two keypoints, we can conclude that

_The process of setting or deleting a property is carried out within the  property scope while process of getting or updating a property goes within the prototype scopes._

## Inheritance using prototypes

Now we will create two constructors out of which the latter will be inherited from the former. Though you might know its class based way of inheritance in ES6 using `extends` and `super` but lets do it the prototype way now based upon our understanding as of now.

Lets first create the independent constructors

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

/**
* Defining function in prototype as it
* is a common functionality shared across 
* all the instances
*/
Person.prototype.greet = function(){
  console.log(`Hi, I am ${this.name} and my age is ${this.age}.`);
}

function Adult(name,age,occupation){
  this.name = name;
  this.age = age;
  this.occupation = occupation;
}

Adult.prototype.greetAsAdult = function(){
  console.log(`Hi, I am ${this.name}, my age is ${this.age} and I work as a ${this.occupation}.`);
}
```

Here we can see that `Adult` can be inherited from `Person` constructor considering that we want the `greet` function to be a part of the constructor.

How do we do that?

* We can call the `Person` constructor using the scope of `Human`. We can use `.call` method available on prototype of `Function` constructor.
* We need to set the `prototype` of `Human` to that of `Person` and constructor to itself. 

Firstly, let's do it for `Adult` class.

```javascript
function Adult(name,age,occupation){
  Person.call(this,name,age);
  this.occupation = occupation;
}

// setting the basic properties of a prototype
Adult.prototype = Object.create(Person.prototype);
Adult.prototype.constructor = Adult;
```
Using `Object.create`, we create an empty object having the prototype properties of `Person.prototype`. This is because later we can define our methods on `Adult.prototype` without touching the object of base constructor's prototype.

Once we have done this much, now we can define the properties on `Adult`'s prototype.
```javascript
Adult.prototype.greetAsAdult = function(){
  console.log(`Hi, I am ${this.name}, my age is ${this.age} and I work as a ${this.occupation}.`);
}
```

Now, let's create an instance of `Adult` and call the functions.

```javascript
let john = new Adult("John",23,"Software Developer");
john.greet(); // Hi, I am John and my age is 23.
john.greetAsAdult(); // Hi, I am John, my age is 23 and I work as a Software Developer.
```

## Conclusion

* Prototypes are objects existing on every function declaration existing within the JS code.
* They form a prototype chain to their parent constructor prototypes until they exist.
* We can declare common functionalities to be shared across all the instances of that constructors within the prototype itself.
* Prototypes play a major role in inheriting properties of base constructors.

## End Notes

If you have reached this section, I hope you have gone through the article xD. Feel free to appreciate or criticise  the post in the discussions. Show some love if it is useful to you.

Would be writing more posts on Javascript concepts, React concepts and some frontend projects which I am working on nowadays.

## References
* https://betterprogramming.pub/prototypes-in-javascript-5bba2990e04b
* https://github.com/leonardomso/33-js-concepts#17-prototype-inheritance-and-prototype-chain