/ Javascript

Inheritance and Prototype Chain in Javascript

Javascript is an interesting programming language. It's functional, yet object-oriented. It's object-oriented, while it doesn't have the concept of class. Javascript is prototype-based and inheritance is done prototypically.

The prototype chain

What is so special about prototype-based inheritance is the use of a prototype chain.

Suppose you are a junior programmer at Google and you are asked to build a new search engine that can beat Google. You'll first figure out a solution from your brain. What if you can't solve it? You shall ask a senior to do you a favor. If the senior cannot help, ask his senior - the assistant chief programmer. You will go for the chief programmer finally if the assistant chief programmer has no idea too. What if the chief programmer can't solve it as well? Ask Larry Page - CEO of Google who invented PageRank. But you know, he is too busy to deal with small stuff from you. Now, you can reject the task saying that Google is unbeatable.

In reality, you can seek help from team members of course. Javascript objects are always so alone that they seldom have friends. They have only parents. The ultimate parent in the ancestor chain is the god. And the god null is usually too busy to answer anything.

Demonstration

Create a inheritance.js (or use a online REPL).
You can find my full "inheritance.js" here.

// Define Rectangle
var Rectangle = function(width, height) {
  this.width = width;
  this.height = height;
};
// Define the public properties and methods of Rectangle (which can be inherited)
Rectangle.prototype.getArea = function() {
  return this.width * this.height;
};

// Define Squre as a "subclass" of Rectangle
var Square = function(width) {
  Rectangle.call(this, width, width);
};
// Inherit the public methods from Rectangle
Square.prototype = Object.create(Rectangle.prototype);

// Instantiate a Rectangle and a Square
var r = new Rectangle(4, 5);
var s = new Square(4);

// Print the properties of the r and s
console.log("r:", r);
console.log("s:", s);

// Print the area of the rectangle
console.log("r area:", r.getArea());
console.log("s area:", s.getArea());

When we run the script, we will get...

r: { width: 4, height: 5 }
s: { width: 4, height: 4 }
r area: 20
s area: 16

We can see that both r and s do not have the getArea property but it can still be called. Why? That's how the prototype chain works. Let's investigate it more deeply.
Add the following to inheritance.js.

// Print the prototype chain of r and s
console.log('r.__proto__:', r.__proto__);
console.log('r.__proto__.__proto__:', r.__proto__.__proto__);
console.log('r.__proto__.__proto__.__proto__:', r.__proto__.__proto__.__proto__);
console.log('s.__proto__:', s.__proto__);
console.log('s.__proto__.__proto__:', s.__proto__.__proto__);
console.log('s.__proto__.__proto__.__proto__:', s.__proto__.__proto__.__proto__);
console.log('s.__proto__.__proto__.__proto__.__proto__:', s.__proto__.__proto__.__proto__.__proto__);

The extra output:

r.__proto__: { getArea: [Function] }
r.__proto__.__proto__: {}
r.__proto__.__proto__.__proto__: null
s.__proto__: {}
s.__proto__.__proto__: { getArea: [Function] }
s.__proto__.__proto__.__proto__: {}
s.__proto__.__proto__.__proto__.__proto__: null

We are accessing the prototype chain using the __proto__ property, which is deprecated. I will explain more in another article. For now, you can see that getArea exists in r and s prototype chain. That's why it can be called even though it is not a direct property of r and s.

You might discover that getArea is found in the lowest level in the prototype chain for r while it is in the second lowest level for s. It is because Square is a "subclass" of Rectangle and it should have its own prototype for the others to extend from it.

As mentioned before, the ultimate parent in the prototype chain for all the object is the null.

Conclusion

Prototype-based inheritance is straight forward. However, there is a few magic to make it happen. One of the wonderful magic is how an object is created from the constructor, which is a function. The magic will be covered in How "new" Works in Javascript.

Read also...

  1. How "new" Works in Javascript
  2. Details of the Object Model