PASZ.COM ARTICLES

A Practical Approach for Implementing Inheritance in JavaScript

Introduction

After much anguish, I have settled upon the following strategy for implementing inheritance in JavaScript. Rather than trying to make inheritance just like it is in Java, we should embrace the dynamicness and simplicity of JavaScript and use it to our advantage. When you build classes in JavaScript, you use the Prototype Object, which allows you to create a Template for a set of Objects. This template for your Object is itself only a simple object, so better not to force overly complex behaviors upon it. My recommendation is to use containment as much as possible instead of inheritance, to keep inheritance hierarchies shallow, and to avoid calling superclass methods from within subclasses methods (super() in java).

I feel my approach allows for many of the advantages of inheritance, without making the code overly complex or resorting to arcane tricks (like __proto__). This approach is also compatible with browsers at least as far back as IE 5.

Note this article assumes you have a basic understanding of JavaScript. See the links at the bottom for recommended reading. Also, this article applies only to versions of JavaScript less than 2. JavaScript 2 implements a new approach to OOP.

Comments and Code Organization

Since the JavaScript interpreter does not enforce good OOP practices, comments are particularly important for organizing your code. The beginning and end of a class declaration should be noted with a comment, and inheritance relationships should be explicitly declared.

/**
* Class Egg - a key component of the breakfast application
* extends abstract base class: Food
**/
.
.
.
/**
* End Class Egg
**/

Classes should be kept in external files, and if possible, each file should only contain a small group of closely related files. You could enforce a one class per file rule on yourself, but this could make your files get unwieldy. Also, there might be performance issues involved with requesting many individual files on a web page(?).

Extending the Superclass

To extend a superclass, add the superclass to the prototype immediately after the constructor. Use the following syntax: SubclassName.prototype = new SuperclassName;

There are a number of techniques for overriding the superclass constructor. Here is where I deviate from most accepted practices. My approach is don't do it. As an alternative, leave the base class constructor empty and create an init method for the base class.

var Mammal = function(){
   //empty constructor
}
Mammal.protoype.init = function(weight, color){
   this.weight = weight;
   this.color = color;
}

var Cat = function(weight, color){
   this.init(weight, color);
}

Cat.prototype = new Mammal;

You can find more sophisticated strategies for calling the superclass constructor, but I like this technique precisely because it is simple!

Also, I highly recommend not creating deep inheritance hierarchies. Going more than 2 or 3 levels deep can make your code extremely confusing, and prone to bugs. Consider using containment instead (i.e. objects that have other objects inside of them).

Methods and Method Overriding

Overriding superclass methods is easy. All you need to do is replace the method in the prototype:

Mammal.protoype.sayHello = function(){
   alert("I am a mammal.");
}

Cat.prototype.sayHello = function(){
   alert("I am a cat.");
}

Calling a superclass method from within a subclass method is trickier, and I recommend avoiding it. In JavaScript, a prototype method is one thing or another, not both. Instead, consider giving the methods different names, as was done when overriding the constructor.

Polymorphism

Here is where JavaScript truly excels... to the point where things can get unwieldy if you're not careful. Unlike C++ or java, JavaScript is loosely typed, so you can overload a method with different types of data. For example:

myClass.prototype.add = function(data){
   alert(data + 1);
}
.
.
.
myClass.add(1); //displays 2
myClass.add("hello"); //displays hello1

When dealing with polymorphism in JavaScript, the typeof operator is your friend. In the example above, you could test to make sure the data parameter is a number. Otherwise, throw an error.

Like Uncle Ben says, "With great power comes great responsibility." Use polymorphism wisely and sparingly. 'nuff said.

Static Methods

No additional effort is required to implement static static methods -- by definition, the methods are part of the prototype, and free for you to call, even if the object has not been instantiated.

Cat.prototype.sayHello = function() {
   alert("I am a cat.");
}
.
.
.
Cat.prototype.sayHello(); // displays the alert

If you want to create an entire static class, then for ease of use you can not even use the prototype object. Only do this if you're not going to instantiate the class.

Cat.sayHello = function() {
   alert("I am a cat.");
}
.
.
.
Cat.sayHello(); // displays the alert

Abstract Classes and Interfaces

JavaScript does not explicitly support Abstract Classes or Interfaces. Rather than trying to implement a hacky solution, you can achieve these sorts of constructs simply with good code organization and commenting.

Conclusion

JavaScript is a simple, yet powerful language. Using inheritance and other OOP techniques give JavaScript developers the tools to add functionality that would be difficult to achieve with procedural programming. When implementing OOP techniques in JavaScript, we should avoid the impulse to try to make things exactly like Java or some other OOP language. OOP in JavaScript is fundamentally different, and developers should find practical ways to take advantage of these differences.

Here are the basic strategies I recommend when building Classes:
1. Keep your class hierarchies shallow
2. Don't rely on calling superclass constructors or methods. Instead just give them different names.
3. Use polymorphism to your advantage, but don't overuse it.
4. Keep code organized and well-commented to help explain how your classes are arranged.

Please send your comments and complaints to avatar@pasz.com

Recommended Links

Object Hierarchy and Inheritance in JavaScript

Classical Inheritance in Javascript

Modeling a Draggable Layer and Loading Dynamic Content to it via XMLHTTP

HOME