It is possible in Javascript (ECMAScript) to do classical single inheritance nested to any depth, without any caveats.

This article concisely explores the mechanism in depth, including flaws in other approaches, internal Javascript algorithms, proper OOP data encapsulation with default constructors, and an optimized inheritance declaration syntax with both Object and Function prototype convenience methods.

3Dize, Inc. logo

Correct OOP for Javascript

by Shelby H. Moore III

Created: Jan. 30, 2006
Updated: Feb. 16, 2006

Incorrect

Among the numerous articles on the topic of inheritance and OOP (Object Oriented Programming) using Javascript, many of them share a fundamental flaw.

For example, let's analyze the the first source code example in the page authored by Gavin Kistner. Note, as of Jan 2006, Gavin's page is linked prominently from the main Mozilla Developer Documentation page for Javascript.

To prove this fundamental flaw, simply add the following example code to Gavin's first example:

	var myPet2 = new Cat('Felix2');
	alert('myPet2 is '+myPet2);            // results in 'myPet is [Cat "Felix2"]'
	myPet2.haveABaby();                    // calls a method inherited from Mammal
	alert(myPet2.offspring.length);        // shows that the cat has two babies now
	alert(myPet2.offspring[1]);            // results in '[Mammal "Baby Felix2"]'

Note that myPet2 then reports that it has two offspring (babies), but it should only have one. The reason is because:

	Cat.prototype = new Mammal();

places a single instance of Mammal in the prototype chain of Cat. Thus, any instances of Cat will modify the same single instance offspring property in Mammal. The parent class's instance members become prototype members, which are shared by all instances.

Correct

The fix to this flaw employs the "masking effect" of the order in which Javascript searches for elements (properties and methods) of an object. When resolving object.identifier, where object.identifier is a reference to a Javascript data type (e.g. Function, Object, Number, or String), then Javascript does the equivalent of:

	function resolve( identifier, object )
	{
		for( var element in object )
		{
			if( element == identifier )
			{
				return object.element;
			}
		}
		if( object.constructor.prototype != null )
		{
			return resolve( identifier, object.constructor.prototype )
		}
		return "undefined";
	}

Thus, if identifier exists in the child class, then it will "mask" any duplicate in the prototype chain. Thus, an obvious "bandaid" to the Gavin's example is to add an offspring property to Cat:

	function Cat(name){
		this.name=name;
		this.offspring=[];
	}

But this defeats the purpose of inheritance. If we need to know the internal datastructure of Mammal (the parent class) in order to implement Cat (the child class), then we don't have data encapsulation and thus we don't have OOP (Object Oriented Programming). Also note that name argument of the constructor of Cat is not passed to Mammal's constructor. This violates the data encapsulation of constructor of Mammal, as it assumes the constructor does nothing more than assignment to the name property on construction.

Others have pointed the way to a generalized solution, which maintains data encapsulation, which is also mentioned in Mozilla's Javascript documentation for Function.call and Function.apply:

	function Cat( name )
	{
		Mammal.call( this, name );
	}

The above code is calling the Mammal constructor function and executing it in the scope of (this is) the new Cat object, which creates all elements of Mammal in Cat object.

Details

In addition to executing the parent's constructor in the scope of the object of the child's constructor, we also have to include the parent's prototype in the child's prototype hierarchy:

	Cat.prototype = new Mammal();

However, Mammal expects a name argument. Depending on what the parent's constructor does, it might generate errors if the expected arguments are not provided. We could solve this either by passing dummy argument(s) and modifying the parent constructor to not accept typeof() == "undefined" argument(s), or by modifying the parent constructor to handle typeof() == "undefined" argument(s). Thus, we provide the equivalent of a default constructor:

	function Mammal(name){
		if( typeof( name ) == "undefined" )
		{
			name = "";
		}
		this.name=name;
		this.offspring=[];
	}

By default, Javascript sets the prototype of the child constructor to an empty Object, but with the child's constructor:

	Cat.prototype = new Object();
	Cat.prototype.constructor = Cat;

When an object of the child's constructor is created, Javascript does the equivalent of:

	var object = new Cat();
	object.constructor = Cat.prototype.constructor;

Yet when assigning an object to a prototype, Javascript does the equivalent of:

	var object = new Mammal();
	Cat.prototype = object;
	Cat.prototype.constructor = object.constructor;

Thus, it is important to insure that the child constructor's prototype.constructor is set to the child constructor:

	Cat.prototype = new Mammal();
	Cat.prototype.constructor = Cat;

Improved

The execution of the parent's constructor in the scope of the object of the child's constructor can be encapsulated by adding a convenience method to the Object.prototype:

	Object.prototype.Inherits = function( parent )
	{
		// Apply parent's constructor to this object
		if( arguments.length > 1 )
		{
			// Note: 'arguments' is an Object, not an Array
			parent.apply( this, Array.prototype.slice.call( arguments, 1 ) );
		}
		else
		{
			parent.call( this );
		}
	}

	Cat.prototype = new Mammal();
	Cat.prototype.constructor = Cat;
	function Cat( name )
	{
		this.Inherits( Mammal, name );
	}

And the prototype inheritance can be encapsulated by adding a convenience method to the Function.prototype:

	Function.prototype.Inherits = function( parent )
	{
		this.prototype = new parent();
		this.prototype.constructor = this;
	}

	Cat.Inherits( Mammal );
	function Cat( name )
	{
		this.Inherits( Mammal, name );
	}

Summary

Simply declare the following methods once:

	Object.prototype.Inherits = function( parent )
	{
		if( arguments.length > 1 )
		{
			parent.apply( this, Array.prototype.slice.call( arguments, 1 ) );
		}
		else
		{
			parent.call( this );
		}
	}

	Function.prototype.Inherits = function( parent )
	{
		this.prototype = new parent();
		this.prototype.constructor = this;
	}

Then declare inheritance easily:

	Cat.Inherits( Mammal );
	function Cat( name )
	{
		this.Inherits( Mammal, name );
	}

	ColoredCat.Inherits( Cat );
	function ColoredCat( name, color )
	{
		this.Inherits( Cat, name );
	}

	Lion.Inherits( ColoredCat );
	function Lion( name )
	{
		this.Inherits( ColoredCat, name, "gold" );
	}

And don't forget to implement a default constructor within each constructor function, by handling input arguments whose typeof() is "undefined".


Copyright (c) 2006, Shelby H. Moore III.
All Rights Reserved.
Free license granted to reprint this page in it's entirety.
Free license granted to quote portions of this page, with an appropriate link or reference to this original copy.
Free license granted to copy the algorithms on this page without restriction.