Introduction to Inheritance and the Prototype Chain in JavaScript (with diagrams)

In this post I’m going to demonstrate how the JavaScript prototype chain works by example, starting with the creation of basic objects and functions and working up to inheritance.  To do this I’ll execute commands in the Chrome JavaScript Console, and then I’ll use diagrams to show the state of the system.  To follow along, open the Chrome JavaScript Console.  Start Chrome and then press Ctrl+Shift+J (or Cmd+Option+J on a Mac).


Objects and the Prototype Chain


First I’ll create an empty object, x:
 > var x = {}
 > dir(x) // used to inspect x.


What’s going on here?
- I’ve created an object, x, and it has a single property, __proto__.
- The value of __proto__ is set to Object.prototype.

I can type the following statement into the console to verify the value of x.__proto__:
 > x.__proto__ === Object.prototype  // true

So what is this __proto__ property, and why does x have it?  In JavaScript, every object is created with a __proto__ property, and its value is always set to Object.prototype.  Object.prototype is a special global object that is created by Javascript on startup.  You can think of Object.prototype as just a regular object.  There is only one Object.prototype in any given session.

NOTE: __proto__ is a hidden property, and although the Chrome JavaScript Console will let you access the __proto__ property directly, some browsers may not.

Next I’ll add some properties:
 > x.x1 = "test x1"
 > Object.prototype.x2 = "test x2"
 > dir(x)


You can see these properties are added to the diagram where you’d expect them to be.

Now let me talk about one of the key concepts in JavaScript, the prototype chain.  If you try to access a property of an object and it does not exist, JavaScript will check if the property exists in __proto__. If it does not, it will check __proto__.__proto__.  This will continue until either the property is found or __proto__ is undefined.

This means we should be able to access x2 directly from x by calling x.x2, even though the property is actually defined in x.__proto__.x2.  Javascript will find it automatically by checking up the prototype chain.  Let’s verify that this is true.
 > x.x1                 // "test x1"
 > Object.prototype.x2  // "test x2"
 > x.__proto__.x2       // "test x2"
 > x.x2                 // "test x2"
 > x.x3                 // undefined

Everything looks good, time to move on to functions.


Functions and the Prototype Chain


Now I’ll create an empty function, f:
 > var f = function() {}
 > dir(f)


There’s a lot going on in this next diagram.  Let’s examine what just happened.
- The __proto__ property of a function always points to Function.prototype.
- Function.prototype is a special global object, kind of like Object.prototype, but it has a __proto__ property that points to Object.prototype.
- functions are also given another property when created, prototype.
- prototype is just an object, so it also has a __proto__ property that points to Object.prototype.

It is also worth noting that some of these objects contain additional properties, but because they are not relevant to the discussion they will not be included in the diagrams.  You can look at these properties in the console if you like.

NOTE: No, you’re not the only one who finds the __proto__ vs. prototype terminology to be extremely confusing.

Now let’s add some more properties:
 > f.y1 = "test y1"
 > Function.prototype.y2 = "test y2"
 > f.prototype.y3 = "test y3"
 > dir(f)



Here is the resulting diagram.  Take a look and make sure everything is where you’d expect it to be.  We’ll do some checks to verify that this is correct.
 > f.y1            // "test y1"
 > f.y2            // "test y2"
 > f.y3            // undefined (not in prototype chain)
 > f.prototype.y3  // "test y3"
 > f.x1            // undefined (property of x)
 > f.x2            // "test x2"

Everything checks out.  Next I’ll demonstrate object creation with the new operator.  


Object Constructors and the new Operator


Let’s start a new session to reset Object.prototype and Function.prototype so that they no longer contain x2 and y2.  To do this in the Chrome JavaScript Console, just reload the webpage.

First, I’ll create a new function intended to be used as an object constructor, A:
 > var A = function (name)
 {
   this.name = name;
 }


A is declared with a capital letter because it is intended to be used as an “object constructor function” (i.e. it will be used to create objects with the new operator, it will never be called like a normal function).  It is not any different than any other function in its implementation, only in its intended use.

Create an object, a1, from A using the new operator:
 > var a1 = new A("a1")


The new operator uses a function to create an object.  Put simply, all of the “this.something” properties defined in the constructor function become properties of the newly created object.  In this example a1 is created with 1 property, name, because this.name was defined in A.

Here’s the interesting part: When an object is constructed from a function using the new operator, its __proto__ value is set to the value of the constructing function’s prototype.  Make sure you understand that last sentence.  You can see it illustrated in the above diagram.  Also, a less cluttered diagram for a1 is shown below.



You can see clearly here that a1 has two objects in its prototype chain, A.prototype and Object.prototype.  Let’s see what happens when we create another object using the same constructor function.
 > var a2 = new A("a2")


The value of a2.__proto__ is also A.prototype.  Because of the prototype chain, any properties or functions added to A.prototype will be accessible from both a1 and a2.  Here’s an example of how this could be used:
 > A.prototype.talk = function() { console.log("I’m an A!") }
 > a1.talk()  // "I’m an A!"
 > a2.talk()  // "I’m an A!"

This brings me to the final topic for this post, inheritance.


Inheritance in JavaScript


in JavaScript, Inheritance can achieved using the prototype chain.  Douglas Crockford refers to the following method of inheritance as “Pseudoclassial Inheritance” because it resembles the type of inheritance seen in a traditional OO language like C++ or Java.  Let’s see how this works.

Start by creating a Base function and a Derived function: 
 > var Base = function() {
   this.thing1 = "Base: thing1";
   this.thing2 = "Base: thing2";
 }

 > var Derived = function() {
   this.thing1 = "Derived: thing1";
 }

We achieve inheritance by setting Derived.prototype to a new object created from Base.  This will make sense later.
 > var b = new Base()
 > Derived.prototype = b

Here’s what the diagram looks like after.  The prototype of Derived has been set to the new Base object, b.



Now let’s create an object, d, from Derived using the new operator:
 > d = new Derived()


This diagram shows the full prototype chain for object d.  Because all of the properties of Base are included in d.prototype, we have an inheritance of sorts.  Let’s try it out.
 > d.thing1  // "Derived: thing1"
 > d.thing2  // "Base: thing2"

Everything looks good.  Any property defined in d will be accessed from d directly.  If the property does not exist in d, but it does exist in b, then it will be accessed from b via the prototype chain.

That’s the basics of the prototype chain and psuedoclassical inheritance, I hope the diagrams helped make everything clear.

Comments

  1. This post is so great to help understanding the prototype chain of javascript clearly and easily. Thanks a lot!

    ReplyDelete

Post a Comment

Popular posts from this blog

Terminate Scripted Minicom Session

CodinGame: Building an AI to play (and win) at "Penguins"

Starcraft 2 Galaxy Map Editor - Change the Size of a Building