Click here to Skip to main content
15,867,141 members
Articles / Programming Languages / Javascript

Real Strong and Easy Inheritance for JS

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
4 Oct 2012CPOL3 min read 15K   17   4
The real answer was the most simple

Introduction

JavaScript is a very powerful language. It looks simple, but it becomes very messy if you don't know what you're doing. Inheritance in JavaScript is the perfect example of it.

Background

I've been looking on the internet for days for the best implementation of inheritance. Let's face it... the most common answers are very complex. There is a very simple method with modern browsers if you understand these three keywords:

  • __proto__
  • prototype
  • constructor

The Core Mechanism of Inheritance: __Proto__

Every object in JS has the hidden property __proto__. When you use one property that is not defined in the object, JS automatically tries to find the property in the object in __proto__:

JavaScript
var o = {};
o.toString();     

In this example, it's obvious that the function toString is not declared in the o object. JS will look in the __proto__ of o to find this function and eventually JS will use: o.__proto__.toString()

This is the mechanism you have to use to inherit one object to an other.

JavaScript
var Mammal = {
	legs: 4
};

var Cat = {
	fur: true
}; 

// Do inheritance
Cat.__proto__ = Mammal;

Cat.legs will be translated into Cat.__proto__.legs at runtime.

A Class Is Not an Object!

There is a real difference between objects and classes that people tend to forget in JS. Classes are not objects.

Classes describe objects like templates. Objects are instances of classes.

The Class Template: Prototype

In JS, you create classes using functions - this may be a little odd at the beginning.

JavaScript
var Mammal = function () { // Create the Mammal class
};      

When using functions as classes, you use another hidden properties: prototype. This is where most of the misunderstanding takes place: prototype and __proto__ are not the same thing! prototype describes what properties will be inherited when the object will be created (with the new mechanism).

In JS, when you do:

JavaScript
var m = new Mammal();  

This is what JS real does:

JavaScript
var m = {}; 
m.__proto__ = Mammal.prototype; 
Mammal.call(m);      

Thus, every properties in Mammal.prototype will be accessible in m.

Let's create our Cat class.

JavaScript
var Cat = function () { // Create the Cat class 
};    

And now, for the inheritance:

JavaScript
Cat.prototype.__proto__ = Mammal.prototype;  

And that's it ! Now when you create a new Cat, it's __proto__ will be Cat.prototype and it's __proto__.__proto__ will be Mammal.prototype so your new born kitten will have access to the Cat and Mammal properties.

Static Properties

It's really easy to add static properties to your class. Instead of setting your property into the prototype, you just set it in the class itself:

JavaScript
Mammal.legs = 4; // all mammals have 4 legs  

The problem is that Cat cannot access the legs property. So we have to have another inheritance:

JavaScript
Cat.__proto__ = Mammal;    

And once again, you don't have to add anything more.

The Contructor Property

I wasn't totally correct when I explained what new does in JS. I've forgotten one line:

JavaScript
var m = {}; 
m.__proto__ = Mammal.prototype; 
m.constructor = Mammal; // the missing line
m.constructor.call(m);    

That means the hidden property constructor holds the class. That is useful to reach static properties.

JavaScript
m.constructor.legs // 4! Mammals always have 4 legs. 

The Construction Chain

When you create a new Cat, you have to explicitly call the parent constructor. This can be achieved with __proto__ and constructor:

JavaScript
var c = new Cat();
c.__proto__; // Cat.prototype 
c.__proto__.__proto__;                      // Mammal.prototype 
c.__proto__.__proto__.constructor;          // Mammal
c.__proto__.__proto__.constructor.call(c);  // Call the Mammal constructor   

All Together

In order to simplify the inheritance process, here's a very helpful yet simple function:

JavaScript
Function.prototype.subClass = function(superClass) {
	this.prototype.__proto__ = superClass.prototype; // Standard inheritance
	this.__proto__ = superClass;            // Class inherit static properties from superClass

	this.prototype.superClass = superClass; // Keep track of superClass
}      

The two first lines provide inheritance for properties and static properties.

The superClass property is a shortcut to the super/parent class - useful to call super/parent function such as constructor.

JavaScript
var Mammal = function () { ... } 
var Cat = function () {
	this.superClass(); // class Mammal constructor
} 
Cat.subClass(Mammal);   

Enhanced Version: The toclass Function

Here's an enhanced version that simplifies dramatically the class creation process in JS.

JavaScript
Function.prototype.toClass = function (superClass, members) {
	if (members == undefined) {
		members = superClass;
		superClass = null;
	}

	if (superClass) {
		this.prototype.__proto__ = 
			superClass.constructor == Object ? 
				superClass :                    // enable inheritance from object
				superClass.prototype;
		this.__proto__ = superClass;            // Class inherit static members from superClass

		this.prototype.superClass = superClass; // Keep track of superClass
		this.prototype.static = this;           // Keep track of statics
	}

	for(var k in members) {
		this.prototype[k] = members[k];
	}

	return this;
};

When using this function, it creates a static property that is an exact synonym of constructor, but it enhances the readability when reaching for static properties.

Here's an example of how to use this function:

JavaScript
Animal = function (){
}
Animal.type = "Animal";
Animal.ShowType = function () {
	console.log(this.type);
}
Animal.toClass({
	talk: function() {
		console.log(this.sound+"!");
	}
});

Dog = function (){
	this.superClass();
}
Dog.type = "Dog";
Dog.toClass(Animal, {
	sound: "bark",
});

Cat = function (){
	this.superClass();
}
Cat.toClass(Animal, {
	sound: "meow",
});

a = new Animal ();
a.talk(); // "undefined!"

d = new Dog ();
d.talk(); // "bark!"

c = new Cat ();
c.talk(); // "meow!"

Animal.ShowType(); // "Animal"
Dog.ShowType();    // "Dog"
c.static.type;     // "Cat"

Points of Interest

Since __proto__ is not exposed in IE, this doesn't work at all with Microsoft browser.

You'll have a similar problem with Safari < 5 and Opera < 10.50.

History

  • 2012-09-29: First publication
  • 2012-10-02: Added list of non working browser
  • 2012-10-04: Minor changes and typo corrections

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO GEB AdoptAGuy
France France
Florent has been learning code since he was 8. Basic, then C, C++, ASM (z80, x86, ARM), Java, PHP, SQL, and finally JS - his last love.
Florent has worked in the video game industry and became an entrepreneur by co-found several projects such as eCommerce, online community and lastly one of the most important dating website in France.

Comments and Discussions

 
SuggestionFor OO adepts Pin
Guirec2-Oct-12 3:46
professionalGuirec2-Oct-12 3:46 
QuestionDOES NOT WORK IN INTERNET EXPLORER Pin
FlorentSteiner1-Oct-12 23:33
FlorentSteiner1-Oct-12 23:33 
QuestionNot too bad Pin
gstolarov1-Oct-12 18:46
gstolarov1-Oct-12 18:46 
AnswerRe: Not too bad Pin
FlorentSteiner1-Oct-12 23:29
FlorentSteiner1-Oct-12 23:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.