Introduction

Amongst the trickiest of concepts to grasp in JavaScript are prototypes.

They are tricky because have a lot of key details to be entertained - not because they are difficult to understand. Many people get stuck in the details about this prototype game so it is highly recommended to take things step by step. Well prototypes are actually easier than they seem to be and at the end of this chapter you will definitely also agree to this fact.

So why waste more time?

What is a prototype?

In simple and comprehensive terms:

A prototype is an object from which other objects inherit properties and methods.

We have emphasised on two key terms - object and inherit.

  1. Prototypes aren't numbers, strings, booleans, arrays or functions. They are pure objects.
  2. Objects inherit properties from their prototype i.e the properties on the prototype are accessbile on the object too. You will understand this term much better as the chapter progresses.

Why care about prototypes?

Consider the following example from the previous constructors chapter:

function Book(name, genre, pages, id) {
    this.name = name;
    this.genre = genre;
    this.pages = pages;
    this.id = id;
    this.displayInfo = function() { return "Name: " + this.name + " Genre: " + this.genre + " Pages: " + this.pages; }
}

var book1 = new Book("Web Designing Guide", "General Knowledge", 156, 3156); // has its own displayInfo() method
var book2 = new Book("JS Skills", "General Knowledge", 500, 1052); // has its own displayInfo() method

Although the code runs perfectly fine, there is a slight problem in it.

Each time we create a new instance of the constructor Book() we create a new method displayInfo for the new object created. What this means is that if we have 100 object instances of Book(), for each of them we have one seperate displayInfo() method stored seperately in memory. Thus we have 100 functions stored which are all exactly the same!

Let's demonstrate this even better using Object.getOwnPropertyNames() method which simply takes an object and shows all its OWN properties. With the above code written add the following lines.

console.log(Object.getOwnPropertyNames(book1)) // ["name", "genre", "pages", "id", "displayInfo"]

console.log(Object.getOwnPropertyNames(book2)) // ["name", "genre", "pages", "id", "displayInfo"]

As you can see for both book1 and book2 we have seperate displayInfo() methods.

So when all the methods are doing the same thing why create them again and again and waste memory. Why not become more memory efficient when the solution is extremely easy? And this is where prototypes introduce themselves.

Prototypes in action

We said earlier that objects inherit properties and methods from their prototypes, so in solving the example above, why not define displayInfo() on the prototype of book1 and book2 and all the instances of Book so that these objects can inherit it.

This part is crucial so let's go over it again. Attention Required!

  1. Objects inherit properties from their prototype object so
  2. Instead of defining displayInfo() on the constructor Book() as we did earlier
  3. Let's define it on the prototype of book1 and book2 which are the instances of Book()
  4. So that they can inherit it.

Didn't even understand yet? Let's implement this in real code now.

function Book(name, genre, pages, id) {
    this.name = name;
    this.genre = genre;
    this.pages = pages;
    this.id = id;
} // Same constructor with removed method

// displayInfo() method on the prototype object
Book.prototype.displayInfo = function() { return "Name: " + this.name + " Genre: " + this.genre + " Pages: " + this.pages; }

var book1 = new Book("Web Designing Guide", "General Knowledge", 156, 3156); // has a displayInfo() method, but not its own
var book2 = new Book("JS Skills", "General Knowledge", 500, 1052); // has a displayInfo() method, but not its own

First we have the same constructor definition as before but now without the displayInfo method. Then we have the main ingredient - the prototype. Now again attention is required over here!

Book.prototype is the PROTOTYPE OBJECT FOR THE INSTANCES OF Book() like book1, book2 etc.

IT IS NOT THE PROTOTYPE OF Book.

Many people confuse the prototype property as taking to an object's prototype like so:

// People confuse that this statement takes to the prototype of book1
book1.prototype.displayInfo = function() { return "Name: " + this.name + " Genre: " + this.genre + " Pages: " + this.pages; }

This confusion is acceptable, since the statement in line 2 above sounds as if taking to the prototype of book1.

Remember one thing that the prototype property is only available to functions.

We write the name of the constructor instead of the name of a single object and then the prototype property.

// Covers all objects - book1, book2, book3 and so on
Book.prototype.displayInfo = function() { return "Name: " + this.name + " Genre: " + this.genre + " Pages: " + this.pages; }

And once we have written the displayInfo() function in the prototype object it can be accessed in all the objects down the chain, which in this case are the instances of Book(). This is the power of prototypes i.e properties may not actually be on the objects themselves but still are accessible owing to prototypal inheritance.

It is good to know how this inheritance works.

How does prototypal inheritance work?

There is no rocket science to how inheritance works in JavaScript. It is very simple. The whole idea is that when a property is called on an object, it is searched for in that object's own properties first. If it's found well and good, but if it's not found then search begins on the object's prototype. If the property isn't even found there then search begins on the prototype of the prototype i.e one level further down. And this goes on until we reach the end which is Object.prototype. Nothing there means the property is undefined

In this way properties can travel across objects that are interconnected in what we call the prototype chain.

This whole section was the trickiest part where many people get stuck. Hopefully you didn't. But how to get to the prototype of an object via the object's name. Developers have thought of that!

Prototype of an object

In the examples above we said that book1.prototype doesn't take to the prototype of book1. So how to get to that prototype? Well we have two ways.

Object.getPrototypeOf() method

This method takes a single argument that is the object whose prototype you wish to see and accordingly returns its prototype object. So for book1 (or even book2) we can now safely and directly visit it's actual prototype like so:

Object.getPrototypeOf(book1); // Takes to the prototype of book1 which is actually Book.prototype

Now as you know that the prototype of book1 is Book.prototype why not even check that do the two objects, Book.prototype and the one returned by Object.getPrototypeOf(book1), match.

Book.prototype === Object.getPrototypeOf(book1); // true

And yes they do! This justifies the fact that Book.prototype is the prototype of book1. Go on and try this with book2!

And let's even clarify further that Book.prototype is not the prototype of the function Book.

Book.prototype === Object.getPrototypeOf(Book); // false

To add properties to the prototype of book1 (and all other instances of Book) we can also use Object.getPrototypeOf(book1) and define stuff on it like so:

Object.getPrototypeOf(book1).displayInfo = function() { return "Name: " + this.name + " Genre: " + this.genre + " Pages: " + this.pages; }
// Object.getPrototypeOf(book1) returns book1's prototype and then we just simply define a method on the returned object

__proto__ property

Another way to access directly an object's prototype is using the __proto__ property. This property is available on every object in JavaScript and takes to its prototype. It does exactly the same thing as the method above - no difference.

book1.__proto__; // Takes to the prototype of book1 which is actually Book.prototype

And in this way you can check for the prototype of any object easily. The prototype of the prototype won't be an issue either.

book1.__proto__._proto;
// First takes to prototype of book1 which is Book.prototype
// And then it takes to the prototype of Book.prototype which is Object.prototype

// Could've also been done using
Object.getPrototypeOf(Object.getPrototypeOf(book1));
Although these methods can be used in actual code they shall be kept for development purposes only. One should try to stick with the prototype property and define stuff using it!

Prototypes inheriting from prototypes

The final stamp in the prototypal power of JavaScript comes from the fact that prototypes can be made to inherit data from other prototypes. All the data on the highest level prototype is available to all the child prototypes and in this way to the lowest level objects. In JavaScript there are already prototypes linked to other prototypes, for example String.prototype inheriting from Object.prototype, however this chaining is present by default in the language.

Here we will see how to explicity  set the prototype of a given prototype. Consider the folllowing example fetching segments of code from the previous chapter:

function Book(name, genre, pages, id) {
    this.name = name;
    this.genre = genre;
    this.pages = pages;
    this.id = id;
}
Book.prototype.displayInfo = function() { return "Name: " + this.name + " Genre: " + this.genre + " Pages: " + this.pages; }

function Coding(name, genre, pages, id, rating) {
    Book.call(this, name, genre, pages, id)
    this.rating = rating;
}

var f1 = new Coding("JavaScript Is Best", "General Knowledge", 500, 1068, 3.5);

console.log(f1.displayInfo()); // throws an error

First we define the constructor Book, then the method displayInfo() on the prototype, then the constructor Coding extending Book, and finally instantiate an object f1 from it. We are defining Coding on top of Book so its instances should have access to displayInfo(). However when we call this method on f1 it throws an error. Why?

This happens because displayInfo() is defined on Book.prototype.

Coding's instances inherit properties from Code.prototype not Book.prototype. Only Book's intances inherit properties from Book.prototype like book1 in the above examples. Book.call() just calls the function Book in the current context of Coding and gets it to emulate the behaviour of a subclass - Coding isn't actually a subclass like that in Java or C#. Remember that JavaScript is not a class-based OOP language!

So what we can do is simply define displayInfo() on Coding.prototype. Now the function is available on f1.

Coding.prototpye.displayInfo = function() { return "Name: " + this.name + " Genre: " + this.genre + " Pages: " + this.pages; }

But why define displayInfo() again? Why not get it inherited? And accordingly we can just write the following statement and solve the problem.

Coding.prototype = Object.create(Book.prototype)

Can you recall this method from a previous chapter? What is it doing over here?

Object.create simply sets Book.prototype as the PROTOTYPE of Coding.prototype. Therefore whatever is in Book.prototype gets inherited by Coding.prototype and likewise by all instances of Coding().

Now if you execute f1.displayInfo() it will work perfectly fine! And you have completed prototypes with this!

In conclusion

Prototypes and constructors are a massive ground in JavaScript object-oriented programming. Many concepts in this unit will repeat in your journeys of other programming languages if you wish to carry out them. This chapter was surely a long and tiring one and digesting such large chunk of information is no doubt difficult. No one expects you to learn all this in one day but if you do, amazing!

Read this chapter as many times as the need be until all concepts are perfectly covered. Take the JavaScript Prototypes quiz and see where you stand!