JavaScript Inheritance – How To Shoot Yourself In the Foot With Prototypes!

by Brian Rinaldi on June 3, 2013

The modern web is always changing, and this article is more than two years old.

By Ryan Anklam

When learning JavaScript, many beginners find it difficult to grasp the intricate details of how JavaScript’s prototypical inheritance works. In this article I will take a look at how to properly use the prototypical inheritance model with parent functions that have instance properties.

A Simple Widget Object

The following code has a Widget parent class that has a messages property and a SubWidget class that uses Widget as it’s parent class. In this case we want each instance of the SubWidget to be initialized with an empty messages array:

var Widget = function( name ){
   this.messages = [];
};

Widget.prototype.type='Widget';

var SubWidget = function( name ){
  this.name = name;
  Widget.apply( this, Array.prototype.slice.call( arguments ) );
};

SubWidget.prototype = new Widget();

Before we set SubWidget’s prototype to a new instance of our Widget Prototype constructor, we get an object graph that looks like this:

JavaScriptPrototypes1

The final line of code sets SubWidget’s parent class to a new instance of the Widget class. Using the ”new” keyword starts to tie everything together by beginning to create the inheritance tree and tying the scope of our objects together. Our object graph now looks something like this:

JavaScriptPrototypes2

Do you see what the problem is yet? Lets create some instances of our subclass to highlight the issue:

var sub1 = new SubWidget( 'foo' );
var sub2 = new SubWidget( 'bar' );

sub1.messages.push( 'foo' ); 
sub2.messages.push( 'bar' );

Now our object graph looks like this:

JavaScriptPrototypes3

Before I talk about the real problem here I’d like to step back and talk about the “type” property on the widget’s constructor.  If it is not initialized by assignment each instance’s “type” property actually points to the constructor’s “type” property.  However, once it is initialized by assigning a value like so: sub1.type = 'Fuzzy Bunny' it becomes a property of the instance, as shown in the new object graph:

JavaScriptPrototypes4

Recognizing the Problem

Our bug should start to become clear now.  Let’s log the value of both sub1 and sub2’s messages array:

var Widget = function(){
   this.messages = [];
};

Widget.prototype.type='Widget';

var SubWidget = function( name ){
  this.name = name;
};

SubWidget.prototype = new Widget();

var sub1 = new SubWidget( 'foo' );
var sub2 = new SubWidget( 'bar' );

sub1.messages.push( 'foo' ); 
sub2.messages.push( 'bar' );

console.log( sub1.messages ); //[ 'foo', 'bar' ]
console.log( sub2.messages ); //[ 'foo', 'bar' ]

If you run this code and look at your console, you will see two instances reading [“foo”, “bar”] . Each object is sharing the same messages array!

Fixing the Problem

It would be tempting to fix this by adding another property to the SubWidget constructor like so:

var SubWidget = function( name ){
  this.name = name;
  this.messages = [];
};

However, what if we wanted to create other objects that extended Widget?  That new object would also need a messages array and pretty soon we have code that is a nightmare to maintain and extend. Additionally, what if we wanted to add other properties to the Widget constcutor that were initialized when instances of its sub-classes were constructed?  This isn’t very reusable or flexible solution.

In order to properly fix this all that needs to be done is to add a single line of code to our SubWidget constructor that will invoke the Widget constructor with the same scope of our SubWidget constructor. To do this we use the apply ()method which will give us the flexibility to add additional arguments to the SubWidget constructor without having to change the call to Widget:

var Widget = function(){
   this.messages = [];
};

Widget.prototype.type='Widget';

var SubWidget = function( name ){

  this.name = name;

  Widget.apply( this, Array.prototype.slice.call(arguments) );
};

SubWidget.prototype = new Widget();

By using the apply() method we are changing the context in which the messages array is created to the instance of the SubWidget. Now when we create new objects each instance has a new instance of the messages array:

var Widget = function( ){
   this.messages = [];
};

Widget.prototype.type='Widget';

var SubWidget = function( name ){

  this.name = name;
  Widget.apply( this, Array.prototype.slice.call( arguments ) );
};

SubWidget.prototype = new Widget();

var sub1 = new SubWidget( 'foo' );
var sub2 = new SubWidget( 'bar' );

sub1.messages.push( 'foo' );

sub2.messages.push( 'bar' );

console.log(sub1.messages); // ['foo']
console.log(sub2.messages); // ['bar']

Running this you will see [“foo”] and [“bar”] returned because our object instances now correctly have their own messages array. Now our object graph is correct:

JavaScriptPrototypes5

This post was originally published at http://blog.bittersweetryan.com/2013/05/javascript-inheritance-howto-shoot.html