Most of us coming from languages with classical class-based inheritance have hard time understanding the javascript inheritance model where there are no classes.
I spent last few days scratching my head over and over and reading tons of different tutorials and finally I believe I grasped the idea. This blog entry is then not only a note of my attempts but also, hopefully, yet another perspective that is possibly useful to someone who has similar problems with understanding the javascript inheritance.
Classical inheritance – C#
Let’s start with an example of classical inheritance in C#. There is a class with public and private data and a subclass which inherits some properties and wants to call base class’ members. Also we want the type system to be aware of types and properly recognize whether or not an instance belongs to a particular type. Specifically: an instance of the base class should not be recognized as and instance of the child class but the instance of the child class has both the child type and base type.
class Program
{
static void Main( string[] args )
{
Person p = new Person( "jan", "kowalski" );
Console.WriteLine( p.ToString() );
Console.WriteLine( p.GetPassword() );
Person p2 = new Person( "jan2", "kowalski2" );
Console.WriteLine( p2.ToString() );
Console.WriteLine( p2.GetPassword() );
Worker w = new Worker( "tomasz", "malinowski", 42 );
Console.WriteLine( w.ToString() );
Console.WriteLine( w.GetPassword() );
Console.WriteLine( p is Person );
Console.WriteLine( w is Person );
Console.WriteLine( p is Worker );
Console.WriteLine( w is Worker );
}
}
// class
public class Person
{
// public data
public string name;
public string surname;
// public constructor
public Person( string name, string surname )
{
this.name = name;
this.surname = surname;
this._password = name;
}
// public method
public override string ToString()
{
return name + " " + surname;
}
// private data
private string _password;
// public method to expose private data
public string GetPassword()
{
return this._password;
}
}
// a subclass
public class Worker : Person
{
private int salary;
// public constructor calls base class constructor
public Worker( string name, string surname, int salary )
: base( name, surname )
{
this.salary = salary;
}
// overridden method calls base class method
public override string ToString()
{
var ps = base.ToString();
return ps + " " + salary.ToString();
}
}
The output of this code is
jan kowalski
jan
jan2 kowalski2
jan2
tomasz malinowski 42
tomasz
True
True
False
True
Inheritance in JavaScript
I’ve found these tutorials to be helpful:
- Why Prototypal Inheritance Matters by Aadit M Shah
- Private Members in Javascript by Douglas Crockford
- Classical Inheritance in Javascript by Douglas Crockford
- Why I don’t use Prototypal Inheritance in Javascript by Sam Elsamman
Start by reading these and come back when you finish.
It turns out that it is not the prototype notion that caused me most probles but rather the fact that there are two possible approaches to inheritance in JavaScript: the constructor inheritance and prototypal inheritance. The remaining part of this entry shows differences between these two – I will show how the above C# snippet converts to JavaScript for both approaches.
Constructor inheritance – JavaScript
The constructor inheritance is a technique where a function, whose name starts with an uppercase letter by convention, acts as a constructor. You have to remember to call it with new to get a new instance. The newly instance “inherits” properties from a so called prototype chain which basically means that there is a list of known instances which play the role of prototypes.
The notion of the prototype is not new in classical object-oriented languages, the Prototype pattern is a well known design pattern. There is a similar concept behind both Prototype pattern in C# and prototype notion in Javascript:
- in a Protype design pattern you have a prototype instance of an object and you use to create new objects out of by cloning the prototype
- when creating a new instance out of a javascript prototype, the runtime does not clone the object but stores the instance of the prototype in a private field of the newly created instance and uses the prototype to resolve members which are not available on the newly created instance.
Since the newly created javascript instance has its prototype which has its prototype which has its prototype …. the runtime is capable of an arbitrary level of “inheritance” just by walking up the inheritance chain. And just like in C# everything ultimately leads to the Object class, the prototype chain in Javascript ultimately ends with …. Object.prototype.
The constructor inheritance snippet is:
// a public constructor
function Person( name, surname )
{
// public members
this.name = name;
this.surname = surname;
// a private member
var _password = name;
// a method to retrieve the private member
this.getPassword = function()
{
return _password;
}
}
// a public instance method
// has to use "this" otherwise members are not available
Person.prototype.toString =
function()
{
return this.name + ' ' + this.surname;
}
var p = new Person( 'jan', 'kowalski' );
console.log( p.toString() );
console.log( p.getPassword() );
var p2 = new Person( 'jan2', 'kowalski2' );
console.log( p2.toString() );
console.log( p2.getPassword() );
// a subclass
function Worker( name, surname, salary )
{
// calls the public constructor of the base class
// using .call and passing "this" as a first argument
// so that it binds to "this" in the called constructor
Person.call( this, name, surname );
this.salary = salary;
}
Worker.prototype = new Person();
// override the public instance method
Worker.prototype.toString =
function()
{
// call the base class implementation
var s = Person.prototype.toString.call( this );
return s + ' ' + this.salary;
}
var w = new Worker( 'tomasz', 'malinowski', 42 );
console.log( w.toString() );
console.log( w.getPassword() );
// test the type system
console.log( p instanceof Person );
console.log( w instanceof Person );
console.log( p instanceof Worker );
console.log( w instanceof Worker );
Prototypal inheritance in JavaScript
Another and more modern approach to inheritance in Javascript is the prototypal inheritance. There are few subtle differences with the former approach:
- there is no constructor function, rather there is an explicit instance of the prototype
- instead of new we are using Object.create which creates a new instance out of the given prototype
- instead of constructor function we have a factory method which can possibly call the factory method of the base class
- because there is no constructor function, instances cannot be tested for being a particular type with the instanceof operator; rather – an extra method is provided
- an auxiliary function extend is introduced which clones the prototype of the base class as the prototype of the child class
// generic "extend" method to clone prototypes
// usage:
// childproto = baseproto.extend( new set of properties )
Object.prototype.extend = function (extension) {
var hasOwnProperty = Object.hasOwnProperty;
var object = Object.create(this);
for (var property in extension)
if (hasOwnProperty.call(extension, property) ||
typeof object[property] === "undefined")
object[property] = extension[property];
return object;
};
// generic "instanceof" replacement
Object.prototype.instanceof = function (prototype) {
var object = this;
do {
if (object === prototype) return true;
var object = Object.getPrototypeOf(object);
} while (object);
return false;
};
// person prototype instance
var person = {
create : function( name, surname )
{
// call the case class factory method
var self = Object.create( this );
self.name = name;
self.surname = surname;
var _password = name;
self.getPassword = function()
{
return _password;
};
return self;
},
toString : function()
{
return this.name + ' ' + this.surname;
}
}
var p = person.create( 'jan', 'kowalski' );
console.log( p.toString() );
console.log( p.getPassword() );
var p2 = person.create( 'jan2', 'kowalski2' );
console.log( p2.toString() );
console.log( p2.getPassword() );
// create an inherited prototype
var worker = person.extend( {
// override the factory method
create : function( name, surname, salary )
{
// call the case class factory method
var self = person.create.call( this, name, surname );
self.salary = salary;
return self;
},
toString : function()
{
var s = person.toString.call( this );
return s + ' ' + this.salary;
}
});
var w = worker.create( 'tomasz', 'malinowski', 42 );
console.log( w.toString() );
console.log( w.getPassword() );
// test the type system
console.log( p.instanceof( person ) );
console.log( w.instanceof( person ) );
console.log( p.instanceof( worker ) );
console.log( w.instanceof( worker ) );
Testing JavaScript snippets
Both Javascript snippets can be tested in a classical way – you create an html page, reference the script and navigate to the page in a browser of your choice.
Another approach that works well for me is to run FireFox, open up an empty tab page and up the javascript console and make sure that the command line is turned on. This gives a nice interactive command line mode where any script can be pasted and executed and results are visible on the debugging console.