Inheritance in JavaScript

Native JavaScript inheritance is prototypical. For one, this means that you don’t inherit from classes, you inherit from objects. You don’t define classes and instantiate them. Instead, you create objects and use them as a “prototype” or “base” for your own object instances.

In JavaScript, this is facilitated with the mechanisms called “prototype” and “constructor”.

Prototype

The object prototype is essentially an object with members. Two things make it’s behavior unique from other objects: 1) prototype is shared between all instances of that type and 2) prototype members are merged with the instance members.

function Page(content) { }
Page.prototype = {
  body: "Default Body Text.",
  render: function() { alert(this.body); }
};
 
//three unique instances of Page...
var page1 = new Page();
page1.body = "Page 1 content."; // creates new member page1.body ... does NOT modify Page.prototype.body
var page2 = new Page();
var page3 = new Page();
 
//different value for "body", but share the same method "render"...
page1.render(); // alerts "Page 1 content."
page2.render(); // alerts "Default Body Text."
page3.render(); // alerts "Default Body Text."
 
//changing .body has no effect on instances...
Page.prototype.body = "New default body text";
 
//every instance now uses the modified value...
page1.render(); // still alerts "Page 1 content."
page2.render(); // now alerts "New default body text"
page3.render(); // now alerts "New default body text"

Understanding the Prototype Chain

Prototype chaining is the method that an interpreter uses to determine which thing you are referring to when you access and object member. This is not the same as object oriented class inheritance. The prototype chain is a chain of actual object instances… not a hierarchy of object definitions.

function NumVal() { this.value = 42; };
NumVal.prototype = {
  precision: function() { return 2; },
  base: 10
};
function IntVal() {};
IntVal.prototype = new NumVal();
IntVal.prototype.somestring = 'Hello World';
IntVal.prototype.precision = 0;
 
var test = new IntVal();
alert(test.base); // check members of "test" ...  not found, 
                  // check members of IntVal.prototype (new instance of NumVal) ... not found
                  // check members of NumVal.prototype ... Found! Return this value.
 
test.hasOwnProperty("base"); // FALSE
test.base = 2; // NOTICE! creates new member on "test" object -- does NOT modify NumVal.prototype!
alert(test.base); // check members of "test" ...  Found! Return this value.
test.hasOwnProperty("base"); // TRUE
 
// Here is one for you to noodle on and figure out on your own...
NumVal.prototype.hasOwnProperty("value"); // FALSE, but why?
IntVal.prototype.hasOwnProperty("value"); // TRUE, but why?

Objects and Arrays in the prototype

Since the prototype is just an object, it can contain more than just functions and discrete values. This is powerful, and can cause bugs and confusion. This can be best described with an example:

function NumVal(value) { this.value = value; };
NumVal.prototype = {
  chars: { sign: '$', dec: '.' },
  toString: function() { return this.chars.sign + this.value.toString().replace('.', this.chars.dec); }
};
 
var num1 = new NumVal(100.25);
var num2 = new NumVal(20.42);
alert(num1.toString()); // $100.25
alert(num2.toString()); // $20.42
 
num1.chars.dec = ','; // check num1 for "chars" ... not found
                      // check NumVal.prototype for "chars" ... Found! Return object.
                      // set "dec" on the returned object
 
// decimal has been replaced by comma for BOTH!
// same effect as if it was written: NumVal.prototype.chars.dec = ',';
alert(num1.toString()); // $100,25
alert(num2.toString()); // $20,42
 
//can prove that the chars object is shared...
alert(num1.chars === num2.chars); // TRUE
 
//override the chars object for num1
num1.chars = { sign: '_', dec: '_' }; // create member "chars" on num1 object
 
// num1 now uses its own "chars" member, instead of climbing the chain to NumVal.prototype.chars
alert(num1.toString()); // _100_25
alert(num2.toString()); // $20,42

Constructor

A constructor is a function. In JavaScript, any function can be a constructor, it simply needs to be called with the “new” keyword.

function Method() { }
 
var obj1 = Method(); // "Method" is executed, and does not return anything
alert(obj1); // undefined
 
var obj2 = new Method(); // a new object is created, and "Method" is applied to the new object.
alert(obj2); // [Object object]

In order to track which function an object is inherited from, every object has an internal “[[constructor]]” property, and this property is tracked up the prototype chain. This is what enables the “instanceof” keyword:

function NumVal() { this.value = 42; };
NumVal.prototype = {
  precision: function() { return 2; },
  base: 10
};
function IntVal() {};
IntVal.prototype = new NumVal();
IntVal.prototype.somestring = 'Hello World';
IntVal.prototype.precision = 0;
 
var test = new IntVal();
 
alert(test instanceof IntVal); // TRUE
alert(test instanceof NumVal); // TRUE
alert(test instanceof Object); // TRUE

One Response to “Inheritance in JavaScript”

  1. blogging on mac

    I would like to express thanks to the writer for rescuing me from this type of situation. Just after exploring throughout the search engines and seeing techniques that were not pleasant, I was thinking my entire life was well over. Living without the presence of solutions to the problems you have sorted out by means of your main short article is a crucial case, as well as the kind that would have badly damaged my entire career if I had not noticed your web blog. Your personal training and kindness in dealing with a lot of stuff was very useful. I am not sure what I would have done if I had not discovered such a point like this. I can also at this time look ahead to my future. Thanks a lot so much for this impressive and results-oriented guide. I want think twice to refer your web blog to any individual who requires care on this area.

    Reply

Leave a Reply