Skip to Navigation | Skip to Content



On Classical JavaScript Inheritance | September 7th, 2006

Ok, I have been a little busy lately (well really busy) but this post has been in the works for a few weeks now so I just want to get it out.

First off, of the half dozen or so requirements that Dean Edwards puts on his Base.js implementation I admittedly only agree with two of them - and you should see why at the end of this post. Those two are:

I want to avoid calling a class’ constructor function during the prototyping phase
I want to achieve the above without affecting Object.prototype

Those are by far the most important points - particular number one since when dealing with the DOM / UI we don’t want things to happen pre-maturely.

Anyway, the reason for this post is actually due to a recent post on Ajaxian about JavaScript inheritance that was interesting to say the least. It reviewed a few different techniques of performing classical inheritance in the prototype base language that is JavaScript. It reviewed simple inheritance using the syntax like

SubClass.prototype = new BaseClass();

where super methods can be called using global references to the BaseClass from the SubClass and call or apply such as

BaseClass.prototype.getName.call(this);

The author also looked at the more advanced technique from Douglas Crockford and finally showed his preferred method.

There are a few points that I would like to add to the discussion. First of all, the authour suggests staying away from syntax such as

function BaseClass() {
  this.getName = function() {
    return "BaseClass";
  };
}

due to the fact that it can cause “SEVERE” memory leaks in Internet Explorer. Closures - which is what is used here - are one of the most useful features of JavaScript and should not be avoided but instead embraced. Indeed, if you lose references to DOM elements in your closures without removing them you can get memory leaks. Whether or not the leak is severe depends on how much memory is taken up by your DOM references of course. Just because something is dangerous does not mean it should be avoided - I am not going to stop drinking scotch any time soon ;)

I also thought that the example he gave to show how the Crockford method is broken was a bit strange. Admittedly, it does seem that the alert from the Crockford method is not correct being (2-1-1), however, I don’t think that the alert from the authors code is correct either. The numbers I would expect to see would be (2-2-1). In fact, with no polymorphism, overrides or other method metadata, who is to really say how it should work?

There were also two very important inheritance techniques that I thought should have certainly been mentioned. One important technique is that developed by Dean Edwards, which _very_ nicely emulates the usage of the super keyword in Java (although he uses base since super is reserved) to provide access to the BaseClass. If you are interested in the Dean Edwards code go check out his blog. The other is the use of global references that refer not to the BaseClass, as the authour showed in his article, but instead to the SubClass; thus, we avoid any problems one may have with making explicit references to the BaseClass within the SubClass code. We can define a global function that extends a SubClass that looks something like this:


/**
* Allows support for inheritance.
* @param subClass {object} The class that will inherit from the base.
* @param baseClass {object} The class that will be inherited from.
*/
nitobi.extend = function(subClass, baseClass)
{
  // Create a new class that has an empty constructor
  // with the members of the baseClass
  function inheritance() {};
  inheritance.prototype = baseClass.prototype;
  // set prototype to new instance of baseClass
  // _without_ the constructor
  subClass.prototype = new inheritance();
  subClass.prototype.constructor = subClass;
  subClass.baseConstructor = baseClass;
  // enable multiple inheritance
  if (baseClass.base)
  {
    baseClass.prototype.base = baseClass.base;
  }
  subClass.base = baseClass.prototype;
}

This is similar to the method over on Kevlindev. Btw, discussion about all of these techniques can be found before the Crockford technique when Googling for “JavaScript inheritance”. I digress. We have also added the prototype chain support for the base keyword. This type of inheritance has met all our needs and enables multiple inheritance by providing global references to the BaseClass through the SubClass itself as well as to the base constructor. To use it one would write something such as this:

SubClass = function() {}
nitobi.extend(SubClass, BaseClass);

Methods in the BaseClass are then called like this:

SubClass.base.someMethod.apply(this, arguments);

Not only is this, IMHO, a clearer syntax than something more esoteric like “arguments.callee.$” but it also has the added advantage of being faster - as I will show in a moment - than something more complicated like being able to use the word “base” directly. Furthermore, by using the Prototype library syntax for defining functions where the prototype of an object is set to an inline object itself such as:

var myClass.prototype = Class.extend({
  someMethod: function() {},
  someProperty: 5
});

it makes it much more difficult to document using tools such as JSDoc. I always find myself missing commas too! Personally, I find the Prototype syntax much more unfriendly, although it is slightly more terse.

As for the performance, just to give an idea of how the different techniques fair I made a simple animal inheritance chain and checked that the inheritance works properly using each method and then measured 1000 class definitions and instantiations with a call to the super / base in the constructors. It was some nonsense like this:

function Animal(name) {
  this.name = name;
};
Animal.prototype.name = "";
Animal.prototype.eat = function() {
  this.say("Yum!");
};
Animal.prototype.say = function(message) {
  alert(this.name + ": " + message);
};
function Cat()
{
  Cat.baseConstructor.call(this, 'cat');
}
nitobi.extend(Cat, Animal);
Cat.prototype.eat = function(food) {
  if (food instanceof Mouse) Cat.base.eat.call(this);
  else this.say("Yuk! I only eat mice - not " + food.name);
};
function Lion()
{
  Lion.baseConstructor.call(this, 'lion');
}
nitobi.extend(Lion, Cat);

Here is what I found for performance using various techniques:

Internet Explorer

Approach Time Relative
Nitobi 320 1
Crockford 340 1.06
ThinWire 580 1.81
Edwards 1360 4.25

Firefox

Approach Time (ms) Relative
Nitobi 360 1
Crockford 460 1.27
ThinWire 2300 6.39
Edwards 3600 10

I was pretty surprised at the horrible performance of the Edwards base.js in Firefox! At any rate, something to consider for those that do things because they want JavaScript to be more like Java ;)

technorati tags:ajax, javascript, performance, benchmark, inheritance, oop

Posted in AJAX, Benchmark, JavaScript, OOP, Performance, Web | 16 Comments » | Add to Delicious | Digg It

This entry was posted on Thursday, September 7th, 2006 at 3:09 am and is filed under AJAX, Benchmark, JavaScript, OOP, Performance, Web. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

16 Responses to “On Classical JavaScript Inheritance”

  1. weston Says:

    Great article. You have summarized the state of javascript inheritance very nicely. I am using this method now.

  2. Dave Johnson Says:

    thanks weston. you have paid your licensing fee for using it right? ;)

  3. musbah Says:

    Hi, I agree with Joshua, the developer of ThinWire about the incorrect output produced by the example using Sugar (Douglas). The output should be (2,2,2) as you would expect that the super-class methods take precedent over child class methods, and therefore getID must return 2 in all cases. Thanks :-)

  4. Dave Johnson Says:

    hi musbah,

    not sure if that logic is quite correct - I recommend checking it out using something like C#. That seems to come up with 2-2-1.

  5. musbah Says:

    hi Dave, i did, i used Java and the answer was 2,2,2 anyway, I am writing an article about this including a new method that i have developed which seems to perform faster and much it’s simpler than all i have seen online. i can send you a copy of the paper when i finish it. but below is the java code i used to check the incorrectness of Sugar. It is the example i used for my paper and i borrowed the idea of Joshua to test Sugar.

    /******************************* Class: Point2D **************************/
    public class Point2D {
    public int x,y;
    public Point2D(int x,int y){
    this.x = x;
    this.y = y;
    }
    public String id(){
    return “1″;
    }
    public String getId(){
    return “”;
    }
    public String toString(){
    String className = this.getClass().getName();
    return className +”=> (X: “+(new Integer(this.x))+”),(Y: “+(new Integer(this.y))+”)”;
    }
    }
    /******************************* Class: Point3D **************************/
    class Point3D extends Point2D{
    private int z;
    public Point3D(int x,int y,int z){
    super(x,y);
    this.z = z;
    }
    public String id(){
    return “2″;
    }
    public String getId(){
    return ” “+super.getId();
    }
    public String toString(){
    return super.toString()+”, (Z: “+(new Integer(this.z))+”)”;
    }
    }
    /******************************* Class: ColorPoint3D **********************/
    class ColorPoint3D extends Point3D{
    private String color;
    public ColorPoint3D(int x,int y,int z,String color){
    super(x,y,z);
    this.color = color;
    }
    public String id(){
    return super.id();
    }
    public String getId(){
    return ” “+super.getId();
    }
    public String toString(){
    return super.toString()+”, (Color: “+this.color+”)”;
    }
    /******************************* Main Method *****************************/
    public static void main (String args[]) {
    Point2D p1 = new Point2D(10,20);
    Point3D p2 = new Point3D(30,40,50);
    ColorPoint3D p3 = new ColorPoint3D(60,70,80,”RED”);
    System.out.println(p1+”\n”+p2+”\n”+p3+”\n\n”+p3.getId());
    }
    }

  6. Andre’s Blog » Blog Archive » links for 2006-09-08 Says:

    [...] Dave Johnson » Blog Archive » On Classical JavaScript Inheritance Dave jumps on the inheritance conversation with some interesting points on performance measurements. (tags: ajax javascript performance benchmark inheritance oop) [...]

  7. Dave Johnson » Blog Archive » links for 2006-09-08 Says:

    [...] « On Classical JavaScript Inheritance Grid V3.2 » links for 2006-09-08 [...]

  8. william Says:

    Hi musbah,

    Could you please tell me more about your inheritance implemenation ??

  9. ООП и JavaScript « шаманские бредни Says:

    [...] …кратко, от Dave Johnson [...]

  10. indir Says:

    great article. you have summarized the state of javascript inheritance very nicely. I am using this method now.

  11. M Says:

    Hi,
    I have tried an different techniques related to JavaScript inheritance subject.
    The code and the explanation are to long to post them here so if anyone is interested to take a look over it you can find it at http://www.dotnetcaffe.net under JavaScript category. Fell free to criticize the code in any way you want…just don’t flame :) .

  12. ChrisS Says:

    Nice articel.

    Can you explain those lines though:

    // enable multiple inheritance
    if (baseClass.base)
    {
    baseClass.prototype.base = baseClass.base;
    }

    Else i would rather write:
    subClass.base.constructor = baseClass;

    instead of
    subClass.baseConstructor = baseClass;

    because base is our pseudo keyword already, and constructor is one too…

    What is still ugly for me, is that you have to write ClassName.prototype.method for each method you want to add.

    A JSON syntax ala
    ClassName.prototype =
    {
    method: function()
    {
    }
    }

    would be clearer. This could be easily achieved, if you write the prototype assignment in a for-in loop.

  13. Dave Johnson Says:

    I don’t use constructor since that is a reserved word in JavaScript.

    Yah I don’t really like the ClassName.prototype.method thing either but for a lot of Java type people it makes sense :S

  14. Chris Says:

    constructor is not a reserved word, neither is prototype.

    http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Reserved_Words

    They are both just properties, but not part of the language, like “while”.

    And here you use the constructor property too:
    subClass.prototype.constructor = subClass;

    Sure, the ClassName.prototype.method thing makes sense, but the JSON syntax makes more sense, and is easier to write.

  15. Dave Johnson Says:

    Sorry yes you are correct they are not reserved words but they are both pre-defined object properties that I prefer not to confuse things with.

    I do use:
    subClass.prototype.constructor = subClass;
    in the code. That is just for completeness as the prototype.constructor property by default points to the class constructor and we just want to maintain that since we blow it away with the line:
    subClass.prototype = new inheritance();

    The other reason I don’t like the JSON syntax is the increased likelihood of the old dangling comma that messes up IE - but to each their own :)

  16. ООП и JavaScript « шаманÑ?кие бредни Says:

    [...] …кратко, от Dave Johnson [...]

Leave a Reply


Search Posts

Archives

Categories