Prototype and Prototypal Inheritance

Content List

  1. Introduction
  2. Function Constructors and Prototypes
  3. Prototype Properties
  4. Changing a functions prototype
  5. Multiple Inheritance
  6. Using Classes to create the prototype chain
  7. Extending built-in Objects

Introduction

In my previous post, i mentioned that ALL javascript objects have a prototype property. This prototype property is only a reference to another object (it does not live on the object). What happens is, if you ask javascript for a property on an object and it does not find it, the prototype object will be searched for that property. If it exists, that value will get returned. If you are familiar with the concept of inheritance, then this is how it works in javascript. Meaning an object in javascript inherits from its prototype (or prototype chain). This is why inheritance in javascript is called prototypal inheritance. Inheritance has a very simple meaning, it means one object gets access to the properties and methods of another object. In javascript that would be its prototype object.

Let us continue using our book object to demonstrate this.

var book = {};

Since you now know how to add properties and methods to objects, lets add some.

var book = {
  title : 'Book Name',
  author: 'Book Author',
  showBookInfo: function(){
    return this.title + ' by ' + this.author;
  }
};

We added 2 properties, title and author, and a method showBookInfo. Now remember i mentioned that ALL objects have a prototype property? Lets call this prototype object proto (for now). Because proto is object, we can added other properties and methods to it as well. Lets add a property called mySecretProperty to this proto object. What happens when you try to console.log this newly added property?

console.log( book.mySecretProperty );

Can you guess? Here is what happens. Book tries to look for a property called mySecretProperty on itself. If it doesnt find it, then it will look inside the proto object attached to it to see if it finds a mySecretProperty property( think of it like this book.proto.mySecretProperty). This is not the exact syntax, but i want you to think of it that way only for now. Our console.log will return the value assigned to it in the proto object. Now, the prototype object (proto) can also point to yet another object, and so on. This can form what we call the prototype chain. A prototype can have another prototype, and that prototype can have yet another prototype. Remember they are objects, so they can have a prototype. So the search will go on and on up the chain looking for that property until the prototype returns undefined. When trying to access a property, we dont need to access the prototype object directly, javascript will look in the chain for us. Its important to remember; the prototype object does NOT live on the object; the object has reference to it. This can be a great thing, because you dont have to put everything on your objects. Imagine have 100 methods on one object. Try instantiating that many times 🙂 (evil laugh)

Lets see some code samples setting the prototype object. We are going to use a property called DUNDER PROTO. A little disclaimer before we do this… you should NEVER EVER! do it in your code. We will be talking about better ways to build objects and set its prototype. Even though the concept of prototypes is simple, it is the downfall of many javascript programmers, so to get the point across i have to break a few rules to get your mind to start seeing prototypes for what they really are before we look at the right way of using them. The next few examples are for demonstration purposes only, because i feel its the best way to understand prototypes.

Modern browsers provide a way for us to directly set the prototype, and that is what we will be looking at first, so you get the idea. Later on will will get into doing it the right way. Again do not use it as it can slow down your javascript application. Just because browser manufacturers have given us this access does not mean we can go ahead an use them in our apps.

Ok, so we already have a book object. Let us create another object

var cinderella = {
  title : 'Cinderella',
  author: 'Charles P.'
};

Now let us set the prototype of cinderella to the book object. This will allow cinderella to access methods on book. Book is now the parent. The way we access the prototype in modern browsers is by using what is call dunder proto. It is written like this ( __proto__ ). Two underscores followed by the word proto, followed by two more underscores. It is done this way to alert the developer that this should NOT be accessed! Its for the browser only. The reason why we use dunder prototype is because when we do cinderella.prototype, it may not always be accessible. Yes all object have a prototype, but they are not always accessible. So browser manufacturers have given us a way to set it on demand! Aren’t they nice? Lets see it in action!

cinderella.__proto__ = book;

Cinderella now inherits from Book. Meaning that we can call the showBookInfo method even though it does not exist on the cinderella object.

console.log(cinderella.showBookInfo());

Our console outputs “Cinderella by Charles P.” Which is exactly what we want. Remember, before showBookInfo does not exist on cinderella, it looks at its prototype which we just set to book, and find that method there.

We need to talk about the this keyword and how that is affected. Notice that even though the showBookInfo function was executed it didn’t not use the title and author in the book object. The this variable knows which object originally executed it. In this case it was the cinderella object. Lets look at accessing other property members. When we call cinderella.title, we get “Cinderella”. If we set the prototype to book, why does it not return “Book Name”. Remember because we are asking it for cinderella’s title, it will first look in that object. IF it does not find it, then alone will it go down the prototype chain and hunt for that property. Lets create another object and set its prototype

var tarzan = {
  title : 'Tarzan'
};
tarzan.__proto__ = book;

Now that this prototype has been set, we can call showBookInfo on the tarzan object. Can you guess what the output will be?

console.log( tarzan.showBookInfo() );

The console returns “Tarzan by Book Author”. Did you guess this? Do you know why? When we call showBookInfo on the tarzan object, it first looks for showBookInfo on itself. Because that method does not exist, it will look in the prototype chain. It finds that book is its prototype, so it searches for this method on that object. It finds it and executes the function. Within that function we have references to this.title and this.author. The tarzan object has a title property, so that value is returned (remember tarzan is the object the executed the code). However the tarzan object does not have an author property. Again javascript looks down the prototype chain and finds that the prototype has that property ‘Book Author’, and therefor it returns that value.

Do you have a better understand of the object prototype? I would love to hear your thoughts on this. Lets move on

You may have heard that almost everything is javascript is an object. And lets not forget what an object is in javascript. See my previous post. Objects are just containers with key-value pairs. Things like undefined, null, boolean, string and number are what we call primitive types in javascript. There is actually a 6th primitive type introduced in ES6 called symbol. Everything else is an object. Now there is a base object that every object inherits from. This base object, is called Object and does NOT have a prototype. Lets create some sample objects and see their prototypes

var myObject = {};
var myFunction = function(){}; 
var myArray = [];

Lets use our console to see what the prototype for this objects are
console.log( myObject.__proto__ );
console.log( myFunction.__proto__ );
console.log( myArray.__proto__ );

Lets look at this one by one. We notice that myObject.__proto__ returns Object {} in the console. This is the base object being returned to us. You should know that this base object has methods and properties attached to it. Because myObject is now this base object, it can access all the base objects properties and methods.

ss1

When i type myObject. in my console, i can see ALL the properties and methods of the base class. This means ALL objects have access to these methods you see in the screenshot. Do any of them look familiar to you? I bet! Lets say i called book.toString() on our book object. As you already know toString does not exist on the book object, however because the base Object is its prototype, it is able to find the toString() method there and execute it.

Lets look at the second console.log. We notice it returns function () {}. This is an empty function. So all functions have a prototype, which is this empty function. Since this is an object, it too has properties and methods on it.

ss2

As can you see from above, the function prototype has properties and methods. That means that ALL functions have access to these. For example every function has a bind, call, apply etc defined on it, because they exist on the prototype chain.

Finally lets look at the myArray objects prototype. Our console returns this, [ ]. Which is an empty array. So the prototype of all arrays is this empty array. Lets see if this array has methods and properties defined on it

ss3

Look at all those methods defined on the array. There is push, length, pop and many many more. This means all arrays in javascript have access to these methods, because they live on its prototype. I ask again, have you seen these methods before? The ONLY reason you are able to call them is because they exist on the objects prototype.

Finally dont forget that the prototype chain is never ending, even though the function and array returned an empty function and empty array as their prototype, you can then again find the prototype of those objects as well. You would however, eventually get down to the base object, which is Object

console.log( myFunction.__proto__.__proto__ ); //returns Object {}
console.log( myArray.__proto__.__proto__ ); // returns Object {}

What do you think will happen if you tried to go up the chain one more time? Yep, you get a null. Remember the base object does not have a prototype. Remember, do not use the dunder prototype. We are about to see how to do it the proper way… now that you understand what is going on when you set an objects prototype. Yiiiihaaa !

Function Constructors and Prototypes

Previously i mentioned that all objects have a prototype but they are not always accessible unless we use dunder proto. Well, functions are a little more special. All functions have a property called prototype. Let me say that in another way. Functions are objects, so of course they have a prototype, HOWEVER they also have a property on them called prototype. This property is NOT the prototype of the function (its a property, i know kind of confusing), ANYWAY, when the function is used as a constructor function to create new objects THEN objects created from this constructor will have a prototype that points to the prototype property of the function. New objects created will automatically get their prototypes set for you. We will look at this in action. Lets go on….

So, not only can you access a functions dunder proto (which you should not use .__proto__), you can also access its .prototype property.

var myFunction = function(){};
console.log( myFunction.prototype ); // returns { }

Notice it returns an empty object. This .prototype property is completely visible to you, meaning you can access it, and are encourage to use this property. Lets see what happens when you try to access this prototype of an object built with JSON.

var tarzan = {
  title : 'Tarzan'
};
console.log(tarzan.prototype); //undefined
console.log(tarzan.__proto__); //returns object

When we call tarzan.prototype we get undefined. It is not visible, because a prototype is more of an inside javascript field. So its not that tarzan does not have a prototype…We dont get access to it unless we use dunder proto (like we saw before).

Lets play with more code

console.log(tarzan.__proto__ === Object); //returns false
console.log(tarzan.__proto__ === Object.prototype); //returns true

Here we wanted to see if what tarzans dunder proto is equivalent to. Clearly it is not equal to the global Object. You should know that the global Object IS a constructor function used to create new objects, so it does have a valid prototype field(all functions have a prototype property, remember?). This is why we see a result of true in our second console.

console.log(typeof Object); //returns function

A common convention used in javascript is to use an uppercase letter for constructor functions. typeof basically returns a string that identifies the data type of an expression. It reports back that Object IS a function.

Getting back to our tarzan example, when we create an object with JSON notation / or object literals, that objects prototype will be set to Object.prototype. This is very very important to note. An objects prototype, is the object instance from which the object is inherited. If we want to change this prototype we can use Object.create passing in the object(prototype) to inherit. Please see the previous tutorial on creating javascript objects(where i first introduced you to Object.create).

Lets see some differences in the definition of prototypes (ones i have found useful)

"A functions prototype is the object instance that will become the prototype for all objects created using this function as a constructor."
"An objects prototype is the object instance from which the object is inherited."

What does all this mean? All functions that are created, get a prototype property attached to it behind the scenes. If that function is then used as a constructor function with the new keyword, the object that is created has a proto property that is pointing to the same object that is the functions prototype.

Lets see that in action…

//lets define a constructor function
function Book(title, author){
 this.title = title;
 this.author = author;
 this.showBookInfo = function(){
    return this.title + ' by ' + this.author;
  }
} 
console.log(typeof Book.prototype); //object
console.log(Book.prototype); // Book { }

Notice that this time we are able to call the .prototype on the constructor function without error. The prototype of the constructor function is the Book object, and you can also see that the typeof Book.prototype is an object. We cannot use this .prototype on a JSON objects, only constructor functions. What do you think we will get if we did this?

console.log(Book.prototype === Object.prototype);

Can you guess? We get FALSE! When we are using JSON Objects / or object literals, its prototype is set to Object.prototype (remember, we saw that before?). However when you are using function constructors, Book will get its own prototype object.

Lets create an object from this constructor function, so we can understand our definition better

var myBook = new Book('tarzan', 'Edgar R.');

For objects created with the function constructor, lets see what its prototype is

console.log( myBook.__proto__ ); // Book { }

Notice here that

Book.prototype === myBook.__proto__;

Remember our definition from earlier? “the object that is created has a proto property that is pointing to the same object that is the functions prototype”. Book.prototype and myBook dundar proto are all pointing to the same object! So changing a property on the prototype will affect the instantiated objects prototype, since they point to the same object.

Book.prototype.bookColor = "red";
Book.prototype.deliverBook = function(){}

It doesnt matter how many instances of Book you created. They will all point to that protoype object.

var mySecondBook = new Book("James Bond", "Ian Fleming");
console.log( Book.prototype === mySecondBook.__proto__ ); //true

Returns true because mySecondBook.__proto__ is poing to the Book object. Notice that because instantiated objects have access to the prototype, it is best to keep properties and methods that will be shared amongst instantiated objects there. We may need to optimize our objects and keep them as small as possible and delegate shared tasks to the prototype. Keeping the methods on the prototype make instantiating objects light and performant. If they dont have a method, they know exactly where to find them We can now optimize our Book construtor to look like this

function Book(title, author){
 this.title = title;
 this.author = author;
}
Book.prototype.showBookInfo = function(){
  return this.title + ' By ' + this.author.
}

The showBookInfo method has now been put into the prototype, and Book is now better optimized. If you instantiate a thousand book objects now, each of them will not contain the showBookInfo on the instantiated object itself. However when it needs it, the object knows how to find it on its prototype.

Prototype Properties

Let see how adding properties affects all objects constructed using that function.

function Book(title, author){
 this.title = title;
 this.author = author;
}
Book.prototype.pageCount = 300;

var book1 = new Book("James Bond", "Ian Fleming");
var book2 = new Book("Cinderella", "Charles P");

Because book1 and book2 share  the same prototype, what happens we you try to access the pageCount

console.log( book1.pageCount === book2.pageCount ); // true

You get true for both of them. How one can override pageCount for each object. The way objects work is that if they cannot find a property on themselves, they will look on the prototype object. In this case since both of them point to the same object, they return the same thing. So if we want book1 to have its unique pageCount, we have to make pageCount immediately available on that object, before it begins to search its prototype.

book1.pageCount = 400
book2.pageCount = 500;
//now lets get the values of book1 and book2 pageCount
console.log(book1.pageCount); //400
console.log(book2.pageCount); // 500
console.log( book1.pageCount === book2.pageCount ); //false

We see that because the pageCount properties are set on the object, they return the pageCount directly from the object and not the prototype. However try to access the pageCount on the prototype:

console.log( book1.__proto__.pageCount );
console.log( book2.__proto__.pageCount ); 
console.log( Book.prototype.pageCount ); // All logs output 300

We see that the prototype still has the 300 value for page count. If we wanted it changed for all objects then we have to change the prototype value itself.

There are some dangers with setting properties i want talk about. If you try to set a property on an object and that property does not exist, the property will still get added onto it. That just how javascript works. For that reason you may want to check to see if that property exists on an object before trying to set it. We use the objects hasOwnProperty to check if a property exists on an object.

if( book1.hasOwnProperty('title') ) {
  book1.pageCount = 400;
}

This will double check to see if that property(title) originally existed on the book1 object instance. If it does, only then will it set the pageCount. If it doesnt, then nothing will happen. If you dont do think, javascript will add that property on the object. This is very important to note. Just because object gives you an answer doesnt mean its right, it may be that the answer is coming from another object down in the prototype chain.

Changing a functions prototype

We saw how to change an objects prototype with Object.create in the previous post. How do we do this for a function. How do we change a functions prototype. Lets go back to our book constructor.

function Book(title, author){
 this.title = title;
 this.author = author;
}
Book.prototype.pageCount = 300;

var book1 = new Book("James Bond", "Ian Fleming");
var book2 = new Book("Cinderella", "Charles P");

console.log(book1.pageCount); //300
console.log(book2.pageCount); //outputs 300

We see that book1 and book2 are return pageCount which is found on the prototype. Let me change this prototype now

Book.prototype = {
  pageCount: 1000;
};

What we have done here is created a brand new object and assigned it to the Book prototype. The object being { pageCount: 1000 }. Please note, it is a brand new object with new memory allocation, WE HAVE NOT OVERRIDDEN the other initial prototype object. It still lives in memory. Lets find out.

console.log(book1.pageCount); //300
console.log(book2.pageCount); //outputs 300

Even though have a new prototype set to that object, book1 and book2 still point to the old object instance. However lets create a new object

var book3 = new Book("Art of Ware", "J.H Huang");
console.log(book3.pageCount); //1000

We see now that book3 outputs the new pageCount property in our new prototype. Because book1 and book2 were created with an object whose prototype was already in memory, they retained that object reference. Book3 was created after a new prototype was created and assigned to Book; at this point all objects created after pointed to the new object reference. You can also look at it this way, Book.prototype.pageCount changes based on where you call it. Call it after the first prototype assignment, and it will be that objects pageCount value. Call after the second prototype was created, and it gets that prototypes value. Simple? Lets get into deep waters.

Multiple Inheritance

Lets look at how to implement multiple inheritance in javascript. In a sense we have already looked at this, since we have talked about the prototype chain. Lets go back to our Books example:

function Book(title, author){
 this.title = title;
 this.author = author;
}
var book1 = new Book("James Bond", "Ian Fleming");

We see that we can look up the inheritance chain.

console.log( book1.__proto__); //returns Book{}
console.log(book1.__proto__.__proto__); //returns Object{}

We see that the prototype of book1 is the Book Object… and the prototype of that is Object { }. The prototype of Object is null. There is no prototype there remember? So already we see some inheritance going on. Lets take this knowledge to new heights. Lets create a new book constructor and create objects that inherit from it

function Book(type){
 this.type = type || 'Backend';
}
Book.prototype.write = function(){
  console.log("Writing a " + this.type + " Book!");
}

We have created a parent class Book and set its prototype. Lets create a JavascriptBook object that inherits from Book

function JavascriptBook(title, author){
 this.title = title;
 this.author = author;
}
JavascriptBook.prototype = Object.create(Book.prototype);
var jsBook = new JavascriptBook("The Good Part", "Doug C");

We created a new constructor function(JavascriptBook) and set its prototype to Book. This way objects instantiated from the JavascriptBook object will have the write method

jsBook.write();

Its worth speaking about why we use Object.create here instead of new. We could have used new. The only difference is that Object.create is NOT going to call the Book constructor function. Its just going to set the function as the prototype and setup the prototype chain. Remember new will ALSO execute the Book function when used, and we just might not want to do that yet. At least not when we are setting the prototype of JavascriptBook. If we were setting up new instances of JavascriptBook, then we want to call new. In setting up multiple inheritance like we are trying to do here, when we create JavascriptBook objects we need to also call Book as well (its parent). This way if anything needs to happen on a Book upon construction, then it can happen. Lets see this in code?

function JavascriptBook(title, author){
 Book.call(this);
 this.title = title;
 this.author = author;
}

This will call the Book constructor, passing in JavascriptBook(this) as the Book being constructed so any Book related initialization can occur if need be. Why are we calling the parent to do any kind of setup/initialization? Because JavascriptBook inherits from Book, maybe anytime we create a JavascriptBook object, we want to setup the book type. Since javascript is a frontend language, we might want to set a type on the parent( the type of book ). This way when we call our write() function it will out the right message(since the parent knows what type it is when it was constructed). We dont want to construct a Javascript book and have it report back as a backend book. Side Note: Yes technically speaking javascript can be used on the backend. But you get the point i am trying to make. In the real world we might want to initialize some things when we construct our objects. Its all depends on what you are creating

function JavascriptBook(title, author){
 Book.call(this, 'Frontend');
 this.title = title;
 this.author = author;
}

So now when we call our parent object we can pass in any parameter it needs to initialize the object. When we call the write() function after creating a JavascriptBook instance, it will output “Frontend” as part of that method. Try it out for youself.

var jsBook = new JavascriptBook("The Good Part", "Doug C");
jsBook.write();

Works like a charm. Lets move on. We now want to see if the jsBook object is really a constructed from JavascriptBook. And also whether it is a Book

console.log(jsBook instanceof JavascriptBook);
console.log(jsBook instanceof Book);

We use the instanceof operator which tests whether an object has in its prototype chain, the prototype property of a constructor. In other words it checks to see if an object from created from a certain constructor function. jsBook was constructed with the JavascriptBook constructor, NOT the Book constructor even though Book is in it prototype chain. We have to make a few changes AFTER we set the prototype of the JavascriptBook object. Lets re-write that.

JavascriptBook.prototype = Object.create(Book.prototype);
JavscriptBook.prototype.constructor = JavascriptBook;

If we dont do this, and we check what constructor jsBook, we get the Book object, which is wrong. It should be JavascriptBook

console.log(jsBook.constructor); // returns Book if we DONT set constructor

Another problem we encounter if we dont set the constructor property is we get the wrong prototype chain

console.log(jsBook.__proto__); // returns Book
console.log(jsBook.__proto__.__proto__); //also returns Book

The first proto should be JavascriptBook, but it returns book. The second is correct. This is all because we did not set the constructor of JavascriptBook. This is the confusing part about doing multiple inheritance. What should you take from here? Whenever you are creating a prototype chain you have to

  1. Set the prototype of the object using Object.create like we saw earlier
  2. Set the constructor of the prototype to the object you are creating
  3. Call the parent class when instantiating the object

Using Classes to create the prototype chain

We can also use class to create the prototype chain. Classes were introduced as of ES6. Lets recreate our Book to be a class

class Book {
 constructor(type){
  this.type = type || 'Backend';
 }
 write(){
  console.log("Writing a " + this.type + " Book!");
 }
}

class JavascriptBook extends Book {
 constructor(title, author){
  super('FrontEnd');
  this.title = title;
  this.author = author;
 }
}
var jsBook = new JavascriptBook("The Good Part", "Doug C");
jsBook.write();

This does exactly the same thing as we did with constructor functions. Do you think its easier to understand? Go ahead and play around with it. Notice we declare our constructor in the class as well as methods of that class. The way we write them is a little different however. You should know the class keyword is only syntactic sugar for Object.create. The syntax is only to appeal to programmers coming from classical languages like Java, C++, etc. If you would like me to go in-depth about the class keyword, do leave me a message.

Extending built-in Objects

Lets see what we have learned about prototypes to see how we can extend built-in objects, likes arrays. Given the following books array

var books = ["tarzan", "jungle book", "cinderella"];

How would you get the last element of this array. You could something like this

var lastBook = books[books.length-1] ;
console.log ( lastBook );

Pretty self explanatory. But what if you wanted this to have a method on books array… like a method called last that automatically returned this value.

Instead of

var lastBook = books[books.length-1] ;

We would have
var lastBook = books.last;

We can use defineProperty like we learned in the previous tutorial

Object.defineProperty(books, 'last', {
  get: function(){
   return this[this.length-1];
  }
});

With this is place we can call books.last

var lastBook = books.last;
console.log(lastBook);

Remember we covered defineProperty in the previous tutorial. Our last method works with no problems, however if we create a new array

var morebook = ["American Ninja", "Superman", "Batman"];

and tried to access morebook.last that would not work! Why? Because the last method only works for one array; books. How can we extend this to all arrays? Here is where prototypes comes in. We need to put this method on the Array objects prototype. All we do is this

Object.defineProperty(Array.prototype, 'last', {
  get: function(){
   return this[this.length-1];
  }
});

Now we can call morebook.last and we get Batman as a result.

OK! Like previous tutorials, that was a lot to cover. Knowledge is prototypal inheritance is vital if you are going to be a true javascript ninja. This topic most definitely causes a lot of confusion among-st javascript developers, so its important you understand it. I certainly do not call myself an expert on the subject. I am still learning, just like you 🙂 If you feel i missed certain things, i urge you to mention them. I am always in a good mood to learn something new. Please follow or reach out to me on social media or reply back on this post if you have to. Thank you, and please come back for updated tutorials.

2 Comments

  1. Kofi, you’ve explained something quite complex so simply I cannot help but smile! Well done 🙌

    1. Smiling back Sonny! I did a lot of homework on this! Made me understand it even more… so i shared it. I hope it helps anyone trying to understand the subject. There is so much more to learn… Stay tuned for my next one on everything Ajax.

Leave a Reply

Your email address will not be published. Required fields are marked *