Prototypes and Prototypal Inheritance in JavaScript: A Beginner's Guide

In this blog we are going to learn about Prototypal Inheritance but before understanding that we need to understand what a prototype is and also we will learn about the prototype chain. This is the very core concept of javascript because the entire inheritance concept in javascript is dependent on the prototype and this behaves a little bit differently than how inheritance works in other languages.

Prototype: In our day-to-day code most of the time we use arrays, functions and objects and those arrays, functions and objects have access to lots of different inbuilt
functions now those functions are not defined by us we did not write those functions then how we can access them the answer is a prototype so all functions or any other properties come via a prototype. So whenever you create any object javascript engine puts these hidden properties and functions to the object and attaches it to your object. In simple words attach objects with those properties to your original object. Let's see the example.

let arr = ["java","python","javascript"];
 /*
    Now with above arr we get access of lots of different methods
    like push(),pop(),unshift() etc . (HOW SO ANS IS PROTOTYPE)
 */

 // Same with objects 

let obj = {
    jobId : "123",
    profile : "Chat gpt developer",
    pay : 0
 }
/*
  Here with above object we can access all the properties that we have
  defined in obj like obj.pay or obj.profile but apart for that we 
  also have different properites attached with this like 
  obj.toString() etc.  (HOW SO AGAIN ANS IS PROTOTYPE)
*/

/*
  Same goes with functions when we define functions we get access of 
  different hidden properties that we did no defined in our program.
*/

 function doNothing(){
 }
 console.log(doNothing.call()); // access of call() possible only because of prototype

Also, we can access that hidden object so in the above example we have taken one array object and function and if we want to access all the hidden properties attach to these then we can do as below.

console.log(arr.__proto__);// this will log all hidden attached properties with arr.

console.log(obj.__proto__);//this will log all hidden attached properties with obj that we never decleared.

// Assume function doNothing from above code
console.log(doNothing.__proto__);//this will log all hidden attached properties with function like call , apply , bind.

The above example is just for understanding but in our day-to-day coding we are simply accessing all the hidden attached properties with the dot operator. Now in javascript, each object has an attached prototype and when you try to console.log(
arr.__proto__)
then it will also print objects in which you will have all the methods like push(), pop() etc but the thing to understand here is that arr.__proto__ is also an object and we learned that in javascript all the object has attached prototype object so that means if we do console.log(arr.__proto__.__proto__) it will also return something and you are right it will print the base Object. the prototype which has different properties attached to it. But now if you try to add one more leave of proto
like console.log(arr.__proto__.__proto__.proto__); then it prints you out a null value. And you see this access of proto on a different level gives access to different prototype objects until it reaches a null value and this chain of prototypes is known as Prototype Chain.

Above we have understood Prototype Chain with an example of arr but it will work the same for functions and objects and eventually it will reach the null value where the chain ends. Now lets us understand it by example.

let parent = {
  id : 1,
  name : 'Elon musk',
  rank : 10,
  getDetails: function (){
   console.log(`${this.name} got ${this.rank}`)
  }
}

let child = {
   name : 'Kapil sharma'
 }

/*
   Never do this never manipulate the prototype object directly like
   this we are just doing to understand the example.
*/
 child.__proto__ = parent; // LINE 1

/*
  If you console.log(chilld.__proto__) it will print entire parent
  object
*/

console.log(child.name); // This will print Kapil sharma because child has property name so it does not need go find at parent object

console.log(child.id);// Child object does not has property id but we have attached parent object with child at LINE 1 so child can have access of all the properties of parent so it will basically print 1.

console.log(child.getDetails());

The child. get details() this one is an interesting case where the child does not have its getDetails() function but it can access it from the parent object inside getDetails()
functions are printing name and rank properties now child object does not have rank properties but it has a name property so it will use the name property from the child object and rank from the parent object end this is known as Prototypal Inheritance where if the current object does not have value then it will borrow values from its parent and for simplicity, we have taken only two object else this chain can be much bigger in some cases.

One interesting thing to learn here is we can attach our own created functions and properties with these prototypes let's understand it by example.

Function.prototype.beHumble(){
 console.log('Be humble');
}
function demo(){
  // nothing
}

function memo(){
 // i will also do nothing
}

Now because of this Function. prototype.beHumble() we have added a new function in the function prototype and that's why if we try to console.log(demo.__proto__) or even we try console.log(memo.__proto__) both of these functions will have access to beHumble() and also in your entire application all the function have access of this beHumble() function and we can do same for arrays and objects by using the
Array.prototype or Object.prototype.

That's all about Prototypes and Prototypal Inheritance. And thanks to Akshay Saini for creating an incredible playlist this blog is just whatever learning I had from there.