Prototypal Inheritance In JavaScript

Prototypal Inheritance In JavaScript

·

10 min read

Okay so this is embarrassing, but for the longest time I did not understand prototypal inheritance in JS , why it exists, what it does, how it works, nothing. In my defence, I started programming in Java and Python, which use base class inheritance, and used these languages more often for school and now work . So, to understand this model of inheritance and why it's used in JS, I needed a complete mental shift. Recently, I've started using TypeScript in my projects and this is when prototypal inheritance really clicked. Here is an explanation of Prototypal Inheritance, how it works as I understand it and how I wish it was explained to me.

To fully understand this article you must have some experience with programming and basic knowledge of core data structures in computer science such as linked lists.

Why Prototypal Inheritance ?

via GIPHY

This is something that really bugged me when I started with JavaScript and now developing professionally. Why the hell have a completely different way of handling inheritance then almost every other language out there (with the exception of functional programming languages like Haskell) ? The reason, because JavaScript needs to run in a browser, and in the browser you are more constrained in terms of how much memory you can use in your application with the exception of chromium based browsers of course ;) . Let's not forget the browser itself is an application and you are essentially running another application within one when you are using a web app particularly in the case of Single Page Applications (SPA) . In most cases and especially today web apps need to be run across many different devices. Regardless if you are on a phone with 1gb of memory or a laptop with 32, these apps are expected to preform just the same.

One way JavaScript addresses this constraint is through prototypal inheritance. With prototypal inheritance, unique object methods exist only in one place in memory even when they are inherited by a "child". In base class inheritance ( the inheritance approach in most programming languages ) child objects when instantiated will duplicate their inherited methods from the parent which means more memory is needed. With prototypal inheritance all you need a reference variable rather than the method itself and thus conserving much more memory.

If you understand prototypal inheritance, this makes sense, however to those new to JavaScript this sounds like total gibberish as it did indeed for me. So, let's break prototypal inheritance in a real code example. Below, we will be creating two classes Foo and Bar. Bar will inherit the incrementBar method from Foo. This is a very simple increment function. We will understand the data-structures that make prototypal inheritance possible between these two classes. Once, you have read the entirity of this article, come back to this section, does it make more sense ?

Introducing Prototypal Inheritance

// this is the constructor
Foo = function(){
   this.bar = 1 
}

// creating a function to increment bar
Foo.prototype.incrementBar = function(){
   this.bar = this.bar + 1
}

Above you'll find a classical ( ES5 ) way of defining a "Class" so to speak in JavaScript. It's a very simple class, all we have done is define a constructor to instantiate a Foo object which will set object variable bar to 1. Then, we defined an increment function for that bar variable. If you copy this code snippet into the console of your browser you should see this in the console.

Now this may look complex at first, but look closely. Defining a JavaScript "class" or function has in reality created an object. Let's create a little visual to help digest this.

I have omitted a lot out of this diagram for the sake of simplicity as there is a lot to get caught up on. When we define a class in JavaScript we create an object which has variables relating to our class definition and a prototype object which among other things store the methods we defined for the class.

Now let's define another class

Bar = function(){
   this.bar = 1 
}

Bar.prototype.__proto__ = Foo.prototype

This might look confusing, but lets copy this into our browser console again.

Look under the highlighted line. It's Foo's incrementBar method ! Alright let's try instantiating Bar and playing around with it.

We called incrementBar on the instantiated Bar object without even defining it in our class definition. When we look, we see that the incrementBar reference on the instantiated Bar object is the same as what was defined in the Foo definition. This is a prime example of prototypal inheritance in action. So, how does this work and why are we able to call our incrementBar method defined in the Foo class on our Bar object.

The Prototype Chain and The __proto__ Special Variable

In order to understand how prototypal inheritance works in JavaScript, we first need to understand a core data-structure called the prototype chain. To do this, we need to introduce a special variable that each and every object in JavaScript has, the __proto__ variable and what it does.

The __proto__ variable has either one or two values

  1. null
  2. A reference to another object

In this way the __proto__ variable in each object forms a singly linked list called the prototype chain. It is this data structure that is the backbone of Prototypal Inheritance. Whenever you call a method or variable on an object, if it doesn't exist on the called object , the JavaScript interpreter will go up the prototype chain by accessing the __proto__ reference. If the method or variable exists on that object then the function is called and the lookup is completed. If it still doesn't exist, then the next object is accessed through the __proto__ variable until either the method (or variable ) is found, or the tail of the prototype chain is reached ( __proto__ is null ), whichever comes first. This is the behaviour we saw when accessing the incrementBar method on the Bar object. The incrementBar method is not defined on the Bar object but it is defined upstream in the prototype chain and so can be accessed.

__proto__ and How It Relates to Functions

As we discussed above, functions are actually objects in JavaScript. Within these objects we have a special reference called prototype which points at another object in which we can define methods and variables.

So when we wrote this JavaScript class

// this is the constructor
Foo = function(){
   this.bar = 1 
}

// creating a function to increment bar
Foo.prototype.incrementBar = function(){
   this.bar = this.bar + 1
}

We created the following data-structure

Now this may seem like a mess of a diagram but let's break it down according to the alphabetical labelling

A. The object resulting from our declaration of the Foo function

B. This is the special reference variable that every Function Object in JavaScript has pointing to the Prototype Object.

C. The Prototype Object has a pointer called constructor which points back to the Foo function object in this case. Every Prototype Object has this pointer which points to their respective Function Object.

D. The Prototype Object itself, this object has two default variables, the __proto__ reference that every JavaScript object has (Explained in F and G ) and the constructor reference variable which is described in C . In this case, we defined the incrementBar method so our prototype has an additional reference to that function object described in E

E. The incrementBar function object, what's really interesting is that this being a Function Object also has the same data-structure as the Foo Function Object

F and G. Each and every object in JavaScript has the special reference variable __proto__ . This will by default point to the Global Prototype Object which you can read more about here .

Awesome, so we now understand the data-structure behind "class" declarations in JavaScript. What happens then when we initialize an object with new?

Three things excluding what happens with the special reference "this" ( This is a subject that deserves an article or two on it's own )

  1. Creates the object based on what was defined in the constructor
  2. Assigns the __proto__ of the newly created object to the Prototype Object of the function object ( Foo.prototype )
  3. Returns the newly created object unless a return is defined in the function definition ( In this case it's an actual function not a class decleration so to speak)

Alright, let's see the effect of this JavaScript line on our data-structure.

let someObject = new Foo();

We are already familiar with most of this diagram. What has changed is H and I

H. Our instantiated Foo object. We can see our constructor has been called and the object variable we defined in it has been set.

I. Our __proto__ reference for this instantiated object has been set to the Foo.prototype object establishing the prototypal chain. If you look closely you can see this important singly linked list data-structure. The nodes are the newly instantiated object, the Foo Prototype Object and the Base JavaScript Object. The edges are the __proto__ pointers I and F.

Let's see this in action !

We can clearly see the prototype chain. The first __proto__ call is I, the second is F and the third is on the base object which has a value of null since this is the end of the chain.

When we call the incrementBar method on the instantiated object JavaScript is going up the prototypal chain until it finds the first reference to incrementBar or reaches the end of the chain (__proto__ = null ) . In this case it is found in the Foo Prototype Object and then called.

Breaking Down Inheritance in JavaScript

We now understand the data-structures behind a class in JavaScript. One of the best ways to learn is by doing. Analyze the second snippet of code containing the Bar definition, we introduced above. Draw it's data-structure on a piece of paper or your preferred medium, can you see how the prototype chain is established when an instance of bar is created ? Can you see why we are able to call incrementBar ? I have provided this below for your convenience.

Bar = function(){
   this.bar = 1 
}

Bar.prototype.__proto__ = Foo.prototype

Alright let's draw the data-structure produced by the code above

Don't worry if you didn't draw the full diagram, I have done this so that you can see the data-structure in totality.

We can see the Bar Function Object (J) and the Bar Prototype Object (K) as we have seen when we created the Foo Object (A). However, there is something different, if you look you can see the __proto__ variable of the Bar Prototype Object is pointing towards the Foo Prototype Object (D). This is because of the line we wrote in the Bar definition.

Bar.prototype.__proto__ = Foo.prototype

When an instance of Bar is instantiated (L) , the __proto__ of variable of that object will point to the Bar Prototype Object. The __proto__ of the Bar Prototype Object points to the Foo Prototype Object which contains the reference to the incrementBar method. Thus incrementBar is upstream of the Bar instance in the prototype chain and so can be called! This is how we can inheritance works in JavaScript.

Continue Learning

If you are like me, you like understanding things you are working with on a fundamental and deep level. This subject is way more vast than what I have described. However, the reason I wrote this blog is not to get into the intricacies of this very large topic but to give a ( hopefully ) simple, yet deep overview. Prototypal Inheritance is a corner stone of the JavaScript language and understanding it makes it much more pleasant to work with.

Here are some resources I loved when learning about Prototypal Inheritance.

If you have any questions or comments, good or bad, I would love to hear from you ! You can reach me on my twitter @thenextmusk