Extending Prototype for Javascript Inheritance : Class.extend()

Many folks around the web are raving about Prototype, a Javascript library written by Sam Stephenson which features “a unique, easy-to-use toolkit for class-driven development and the nicest Ajax library around.” (I stole that line from the prototype site).

Prototype’s popularity is certainly due in part to it’s inclusion in current web darling Ruby on Rails, but that ought not to slag the package (or rails) one bit — I’ve used prototype a fair bit over at GeoBirds, and have become rather reliant on it. In many ways, it solves a ton of problems I didn’t realize I had. Heck, by simply shortening document.getElementById to $, prototype has saved me countless keystrokes, although one might rightly suggest I should have written myself a helper function earlier.

If you’re like me, however, you’re still rather troubled by prototype’s claim of “class-driven development” in Javascript. In fact, you’re probably a little fuzzy on the differences between object-oriented and prototype-based languages (hey, is that why they called it prototype?), and, if you’ve investigated them, you find approaches to enabling classical object-oriented development in Javascript rather unintuitive. (See here, here, and here, all good stuff, but rather painful to use).

Enter prototype’s solution: the Class object and Object.extend() method. Sam Stephenson uses them throughout the library (check out the source!), and they allow users like us to do simple object-oriented programming. For example:

// create an empty Person class
var Person = Class.create();   
 // define the Person interface     
Person.prototype = {                 
    initialize: function(name) {
        this.name = name;
    },
   
    show: function() {
        alert(this.name);
    }
});

var myperson = new Person(“Ken”);
// alerts "Ken"
myperson.show();

and

// create an empty Employee class
var Employee = Class.create()
// extend Person                         
Object.extend(Employee.prototype,Person.prototype);     
// extend Employee interface
Object.extend(Employee.prototype, {                 
    initialize: function(name,salary) {
        // super constructor
        Person.prototype.initialize.call(this, name)
        this.salary = salary;
    },

    show: function() {
        alert(this.name + ‘ earns ‘ + this.salary);
    }
});

var myemployee = new Employee(“Ken”, 100000000)
// alerts "Ken earns 100000000"
myemployee.show();

Not bad. No offence, but, in my opinion, prototype beats other Javascript OO approaches I’ve encountered. Still, I’m troubled that we have to define class creation, inheritance, and interfaces separately. Whatever happened to this?

class Person {
    protected var name;
   
    public Person(thename) {
        name = thename
    }

    public String show() {
       return name;
    }
}

class Employee extends Person {
    protected var salary;
       
    public Employee(thename,thesalary) {
        super(thename);
        salary = thesalary;
    }
   
    public String show() {
           return name + ‘ ‘ + salary;
    }
}

Seems a whole lot more intuitive to me, but, of course, that was Java, and the only relationship between Java and Javascript is a name suggesting either an Indonesian island or my early-morning brew. Luckily, a small extension to prototype gets us something similar. I call it Class.extend()

Object.extend(Class, {
    extend: function(source,additions) {
        var newclass = Class.create();
        if ($C(source)) {
            Object.extend($C(newclass),$C(source));
        }
        Object.extend($C(newclass), additions);
        return newclass;
    }
});

function $C(object) { return object.prototype; }

Explain? Well, Class.extend() simply combines all the class definition steps of our earlier Employee extends Person example.

Using Class.extend, we can now say the following:

var Person = Class.extend({}, {
    initialize: function(name) {
        this.name = name;
    },
   
    show: function() {
        alert(this.name);
    }
});

var Employee = Class.extend(Person, {
    initialize: function(name,salary) {
        $C(Person).initialize.call(this, name);
        this.salary = salary;
    },

    show: function() {
        alert(this.name + ‘ earns ‘ + this.salary);
    }
});

Try it out (code). It’s a small extension, but I find Class.extend() saves me a ton of hassle. In fact, I now use Class.extend() by default and can nearly pretend I’ve never heard of object prototypes.

5 Comments »

  1. Charles Wang said,

    July 7, 2006 @ 3:55 pm

    This is not real dynamic Class level inheritance. Suppose BaseClass introduces a new method by calling Object.extend, the new method won’t be inherited automatically in SubClass.

  2. keifir said,

    August 17, 2006 @ 3:26 pm

    Great article! It saved me tons of time. I hope I won’t need ‘dynamic inheritance’ - that would be a big pain for maintenance.

  3. Andrew Saziniv said,

    April 10, 2007 @ 3:22 am

    There is another approach to implement JavaScript inheritance - a “lazy” inheritance which has all benefits of “prototype” based approach like typed classes, but also eliminates necessity to declare external scripts in proper order and automatically resolves and loads (if necessary) dependencies to external scripts that contains related classes.
    This approach is supported by JSINER library - you can find more about it on http://www.soft-amis.com/jsiner/inheritance.html

  4. ken said,

    April 10, 2007 @ 10:02 pm

    Andrew:

    Great comment, and great work on the JSINER library. I sympathize with your point that typed classes of the sort used in Sam Stephenson’s Prototype library necessitate maintaining a proper loading order. I’ve had to deal with this problem a number of times. Lazy inheritance of the sort you describe solves this problem.

    That said, including an extra library like JSINER adds overhead to a website. As a developer, you’ve got to make a choice…

    For folks already using Prototype library, and Class.extend is a handy 2-function addition which makes coding easier, that’s all. It doesn’t change any inheritance mechanisms…

  5. Kevin Dahlhausen said,

    May 31, 2007 @ 12:36 pm

    Thanks for posting this. It did the trick - I was having trouble finding documentation on how to subclass prototype classes.

RSS feed for comments on this post · TrackBack URI

Leave a Comment