JavaScript Prototypes and Prototype Chain

July 20, 2018
js prototypes inheritance prototype chain objects

We all know JS has objects, for example:

var obj = { name: 'obj' };

and we can access/create/update/delete properties on object obj. But before doing any further operations on it, we can notice that obj already has some properties like valueOf, toString, etc.that we’ve never assigned. Now we have to understand how prototypal inheritance works.

Function as a constructor

In JavaScript, there’s really no difference between a “regular” function and a constructor function. They’re actually all the same. But as a convention, functions that are meant to be used as constructors are generally capitalized.

Let’s create a constructor function called Cat:

function Cat (breed){
    this.breed = breed;
}

typeof Cat.prototype; // "object"

Fun fact here, all functions in JS are also objects, which means that they can have properties. And as it so happens, all functions have a property called prototype, which is also an object.

To make an instance of Cat, we can use the new keyword to “construct” an object from this constructor function:

let kitty = new Cat ('Scottish fold');
kitty.breed; // "Scottish fold"

Prototypal inheritance

As a cat person, I just couldn’t resist the soothing sound of “meow”, now that we have an object kitty, how can we make it “meow” just like any other cat does? Remember that all functions have a prototype property right? Actually, the object kitty and any other object constructed from Cat will gain indirect access to Cat.prototype. By placing meow on Cat.prototype, we made it available to all instances of Cat.

Cat.prototype.meow = function(){
    console.log("Meowwww");
}
kitty.meow(); // "Meowwww"

Even though the object kitty constructed from Cat does not have a meow property on itself, it was able to access Cat.prototype and thus invoke Cat.prototype.meow with this being implicitly set to kitty, which results in “Meowwww” being logged to the console.

This form of code reuse is known as prototypal inheritance, since an instance has an “invisible link” back to their parent object.

Prototype chain

The “invisible link” mentioned above is referred to as the prototype chain. What actually happens when I write kitty.meow() is this:

  1. The JS engine looks for a property called meow on our kitty object.

  2. It doesn’t find one, it looks “up the prototype chain” to kitty’s parent, which is Cat.prototype.

  3. It finds Cat.prototype.meow, and calls it with this bound to kitty.

The traversal algorithm consults the object’s prototype when it cannot find the desired property on the object. If it finds the property on the prototype, the traversal stops. Otherwise, it will consult the prototype of the prototype, and so on, until it finds the property or it reaches the end of the prototype chain.

A function’s prototype

Going back to our constructor function Cat, does Cat.prototype refer to the function Cat’s prototype?

Nope!

Object.getPrototypeOf(Cat) !== Cat.prototype; // true

The actual prototype of Cat is Function.prototype.

Object.getPrototypeOf(FunctionName) === Function.prototype; // true
FunctionName.__proto__ === Function.prototype; // true

// Our cat example
Object.getPrototypeOf(Cat) === Function.prototype; // true
Cat.__proto__ === Function.prototype; // true

This is bacause very function in JavaScript is a Function object, and thus it has a prototype of Function.prototype.

Function.prototype offers common utilities like call, bind and apply which can be accessed from Cat and other functions.

And similarly, the prototype of Function.prototype is also Object.prototype, since after all, the Function object is still an Object instance.

Resources:

Prototypal Inheritance in JavaScript

A Definitive Guide to JavaScript Prototypes

MDN

comments powered by Disqus