Inheritance.js - Javascript OO Ruby Style

Inheritance.js is a plugin for the Prototype JavaScript library that provides developers with a complete, unobtrusive, Ruby inspired, classical inheritance model. Using Inheritance.js, developers are given the power to easily define classes, sub-classes, and mix-ins, with a sprinkle of syntax sugar. The process of overriding methods is simplified; Inheritance.js uses a properly scoped parent method that is made available only inside an overriding method. To top it off, Inheritance.js accomplishes all this without adding any additional properties or methods to the developer’s instantiated objects.

Goals and ideas

Anybody who works with a lot of javascript, especially those coming from a strong OO background, knows that when it comes down to creating a nice object hierarchy using traditional javascript is very bulky and time consuming. For a more complete description of the problem see my earlier article on this very subject, Object Inheritance with Javascript.

I wanted to create a small library that would I could use to help simplify the OO pains, and ultimately make my javascript code a lot more maintainable and way easier on the old eyes to look at. To start with, I had the same goals as Dean Edwards.

http://dean.edwards.name/weblog/2006/03/base/

  • I want to easily create classes without the MyClass.prototype cruft
  • I want method overriding with intuitive access to the overridden method (like Java’s super)
  • I want to avoid calling a class’ constructor function during the prototyping phase
  • I want to easily add static (class) properties and methods
  • I want to achieve the above without resorting to global functions to build prototype chains
  • I want to achieve the above without affecting Object.prototype

However, since I am a firm believer in making my javascript code unobtrusive, I wanted to extend that behavior to my library.

  • I want to achieve all of the above without adding additional baggage to final object.

More concisely, I don’t want to end up adding additional properties or methods to the classes that are defined using my library. The instantiated objects should look and behave exactly how the author has written them, and not how my library needs them to look or behave.

Lastly, I added two more requirements, mainly just for personal reasons.

  • I want a more Ruby like feel to creating and working with Objects.
  • I want to achieve all of the above while maintaining backward compatibility with Prototype.

Working with classes

Defining a class is a simple process, you just need remember one method: Class.extend.

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

    toString: function() {
      return this.name;
    }
  });

The initialize method acts like a constructor but is in fact simply an initializer method. In traditional javascript, the constructor function must serve two purposes. In most cases it will act as an instance initializer. However, the constructor function must be executed in order to create a prototype object for any child classes. In such cases, the constructor function is now also acting as a prototype initializer. This double duty often results in very subtle bugs, that can often be hard to find.

Lucky for you, you don’t need to worry about this when using my library. Inheritance.js will handle the responsibility of creating the real construction function for you. By doing this, it knows when this constructor function is acting like an instance initializer or as a prototype initializer and will make the appropriate decision about whether or not to call your defined initialize function.

Inheritance simplified

To extend a class, you use the same Class.extend method as before. This time however you add a reference to the class you are trying to extend. The following code will create a new class Employee that inherits functionality from the Person class we previously defined.

  Employee = Class.extend(Person, {
    initialize: function(name, dept) {
      this.parent(name);
      this.dept = dept;
    },

    toString: function() {
      return this.parent() + " works in " + this.dept;
    }
  });

“Whoa, hang on here. What is this ‘this.parent(name);‘ method call? I never defined a parent method.” That’s quite right, you didn’t write that method. The parent method is a little, but very powerful helper method my library is providing for you. Now before you start complaining that I have violated one of my own design requirements just let me explain a little further. I have managed to provide the parent method in the proper scope. When calling this.parent(name); inside the initialize method, it will call the initialize method in the parent class. In this way parent acts just like super found in many other languages. Trying to use the parent method outside the scope of a method definition, as seen in the following example, will result in an Javascript error.

  var joe = new Employee();
  // calling parent will throw an error
  joe.parent();

Furthermore, if you were to loop over every element in Employee, Employee.prototype or it’s instance joe you won’t find any references to parent at all.

Using Mix-ins

Mixins are a very powerful feature found in Ruby, and a few other languages. Although not directly found in the Javascript language, the language is dynamic enough that we can make it work with relative ease. In fact, it’s easier than trying to get object inheritance to work.

To begin with, we’ll define a mixin as something that cannot have instances. So how to we do that in javascript?

  Comparable = {
    greaterThan: function(other) {
      return (this.compareTo(other) == 1);
    },

    lessThan: function(other) {
      return (this.compareTo(other) == -1);
    },

    greaterThanEqualTo: function(other) {
      return (this.compareTo(other) >= 0);
    },

    lessThanEqualTo: function(other) {
      return (this.compareTo(other) <= 0);
    },

    equals: function(other) {
      return (this.compareTo(other) == 0);
    },

    compareTo: function(other) {
      // expects this to be implemented and 
      // return -1, 0, 1 for less than, equal to, 
      // and greater than respectively

      // for now we throw an informative error
      throw "method 'compareTo' not defined"
    }
  };

We now a javascript object called Comparable, that cannot have any more instances. In other words, we’ve created our mixin. So now what, how can we mix that functionality into one of our classes? This is where I created a little syntax sugar to make things really simple.

  Employee = Class.extend({

    // mmmm, yummy syntax sugar
    include: Comparable,

    initialize: function(name, dept) {
      this.name = name;
      this.dept = dept;
    },

    compareTo: function(other) {
      // here we define the missing compareTo
      // method from Comparable
      if (this.name > other.name) {
        return 1;
      } else if (this.name < other.name) {
        return -1;
      } else {
        return 0;
      }
    },

    toString: function() {
      return '[' + this.name + ', ' + this.dept + ']';
    }
  });

When you define you classes, you can add an include property. This property can reference a single mixin or an array of mixins. For example:

  Employee = Class.extend({

    include: [Comparable, Debug],

    // ...
  });

Inheritance.js will take those object references and mix any methods defined in them into your class. It will then remove the include property from the class definition. Thus, cleaning up that little bit of syntax sugar.

Plays well with prototype.js

Inheritance.js has been designed to work as a plugin for Prototype. You just need to remember to include prototype.js before you include inheritance.js in your web application. After that, you can take advantage of all the capabilities I’ve just talked about. If already have some legacy code that is working with prototype, don’t worry you can mix and match. Your old scripts will work exactly the same way they did before, but with some new powers.

  // the old prototype way still works
  Person = Class.create();
  Person.prototype = Object.extend(Person.prototype, {
    initialize: function(name) {
      this.name = name;
    },

    toString: function() {
      return this.name;
    }
  });

  // mixed in with the new style
  Employee = Class.extend(Person, {
    initialize: function(name, dept) {
      this.parent(name);
      this.dept = dept;
    },

    toString: function() {
      return this.parent() + " works in " + this.dept;
    }
  });

The Goods

If you need a little more convincing you can head on over to some example pages to have a look at all this in action. To get started right away, you can grab the latest distribution from this link.

Just the .js

Example pages

And the complete source code, including unit tests, is available via darcs repository use:

darcs get http://twologic.com/repos/inheritance/

Inheritance.js - Javascript OO Ruby Style

Inheritance.js is a plugin for the Prototype JavaScript library that provides developers with a complete, unobtrusive, Ruby inspired, classical inheritance model. Using Inheritance.js, developers are given the power to easily define classes, sub-classes, and mix-ins, with a sprinkle of syntax sugar. The process of overriding methods is simplified; Inheritance.js uses a properly scoped parent method that is made available only inside an overriding method. To top it off, Inheritance.js accomplishes all this without adding any additional properties or methods to the developer’s instantiated objects.

Goals and ideas

Anybody who works with a lot of javascript, especially those coming from a strong OO background, knows that when it comes down to creating a nice object hierarchy using traditional javascript is very bulky and time consuming. For a more complete description of the problem see my earlier article on this very subject, Object Inheritance with Javascript.

I wanted to create a small library that would I could use to help simplify the OO pains, and ultimately make my javascript code a lot more maintainable and way easier on the old eyes to look at. To start with, I had the same goals as Dean Edwards.

http://dean.edwards.name/weblog/2006/03/base/

  • I want to easily create classes without the MyClass.prototype cruft
  • I want method overriding with intuitive access to the overridden method (like Java’s super)
  • I want to avoid calling a class’ constructor function during the prototyping phase
  • I want to easily add static (class) properties and methods
  • I want to achieve the above without resorting to global functions to build prototype chains
  • I want to achieve the above without affecting Object.prototype

However, since I am a firm believer in making my javascript code unobtrusive, I wanted to extend that behavior to my library.

  • I want to achieve all of the above without adding additional baggage to final object.

More concisely, I don’t want to end up adding additional properties or methods to the classes that are defined using my library. The instantiated objects should look and behave exactly how the author has written them, and not how my library needs them to look or behave.

Lastly, I added two more requirements, mainly just for personal reasons.

  • I want a more Ruby like feel to creating and working with Objects.
  • I want to achieve all of the above while maintaining backward compatibility with Prototype.

Working with classes

Defining a class is a simple process, you just need remember one method: Class.extend.

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

    toString: function() {
      return this.name;
    }
  });

The initialize method acts like a constructor but is in fact simply an initializer method. In traditional javascript, the constructor function must serve two purposes. In most cases it will act as an instance initializer. However, the constructor function must be executed in order to create a prototype object for any child classes. In such cases, the constructor function is now also acting as a prototype initializer. This double duty often results in very subtle bugs, that can often be hard to find.

Lucky for you, you don’t need to worry about this when using my library. Inheritance.js will handle the responsibility of creating the real construction function for you. By doing this, it knows when this constructor function is acting like an instance initializer or as a prototype initializer and will make the appropriate decision about whether or not to call your defined initialize function.

Inheritance simplified

To extend a class, you use the same Class.extend method as before. This time however you add a reference to the class you are trying to extend. The following code will create a new class Employee that inherits functionality from the Person class we previously defined.

  Employee = Class.extend(Person, {
    initialize: function(name, dept) {
      this.parent(name);
      this.dept = dept;
    },

    toString: function() {
      return this.parent() + " works in " + this.dept;
    }
  });

“Whoa, hang on here. What is this ‘this.parent(name);‘ method call? I never defined a parent method.” That’s quite right, you didn’t write that method. The parent method is a little, but very powerful helper method my library is providing for you. Now before you start complaining that I have violated one of my own design requirements just let me explain a little further. I have managed to provide the parent method in the proper scope. When calling this.parent(name); inside the initialize method, it will call the initialize method in the parent class. In this way parent acts just like super found in many other languages. Trying to use the parent method outside the scope of a method definition, as seen in the following example, will result in an Javascript error.

  var joe = new Employee();
  // calling parent will throw an error
  joe.parent();

Furthermore, if you were to loop over every element in Employee, Employee.prototype or it’s instance joe you won’t find any references to parent at all.

Using Mix-ins

Mixins are a very powerful feature found in Ruby, and a few other languages. Although not directly found in the Javascript language, the language is dynamic enough that we can make it work with relative ease. In fact, it’s easier than trying to get object inheritance to work.

To begin with, we’ll define a mixin as something that cannot have instances. So how to we do that in javascript?

  Comparable = {
    greaterThan: function(other) {
      return (this.compareTo(other) == 1);
    },

    lessThan: function(other) {
      return (this.compareTo(other) == -1);
    },

    greaterThanEqualTo: function(other) {
      return (this.compareTo(other) >= 0);
    },

    lessThanEqualTo: function(other) {
      return (this.compareTo(other) <= 0);
    },

    equals: function(other) {
      return (this.compareTo(other) == 0);
    },

    compareTo: function(other) {
      // expects this to be implemented and 
      // return -1, 0, 1 for less than, equal to, 
      // and greater than respectively

      // for now we throw an informative error
      throw "method 'compareTo' not defined"
    }
  };

We now a javascript object called Comparable, that cannot have any more instances. In other words, we’ve created our mixin. So now what, how can we mix that functionality into one of our classes? This is where I created a little syntax sugar to make things really simple.

  Employee = Class.extend({

    // mmmm, yummy syntax sugar
    include: Comparable,

    initialize: function(name, dept) {
      this.name = name;
      this.dept = dept;
    },

    compareTo: function(other) {
      // here we define the missing compareTo
      // method from Comparable
      if (this.name > other.name) {
        return 1;
      } else if (this.name < other.name) {
        return -1;
      } else {
        return 0;
      }
    },

    toString: function() {
      return '[' + this.name + ', ' + this.dept + ']';
    }
  });

When you define you classes, you can add an include property. This property can reference a single mixin or an array of mixins. For example:

  Employee = Class.extend({

    include: [Comparable, Debug],

    // ...
  });

Inheritance.js will take those object references and mix any methods defined in them into your class. It will then remove the include property from the class definition. Thus, cleaning up that little bit of syntax sugar.

Plays well with prototype.js

Inheritance.js has been designed to work as a plugin for Prototype. You just need to remember to include prototype.js before you include inheritance.js in your web application. After that, you can take advantage of all the capabilities I’ve just talked about. If already have some legacy code that is working with prototype, don’t worry you can mix and match. Your old scripts will work exactly the same way they did before, but with some new powers.

  // the old prototype way still works
  Person = Class.create();
  Person.prototype = Object.extend(Person.prototype, {
    initialize: function(name) {
      this.name = name;
    },

    toString: function() {
      return this.name;
    }
  });

  // mixed in with the new style
  Employee = Class.extend(Person, {
    initialize: function(name, dept) {
      this.parent(name);
      this.dept = dept;
    },

    toString: function() {
      return this.parent() + " works in " + this.dept;
    }
  });

The Goods

If you need a little more convincing you can head on over to some example pages to have a look at all this in action. To get started right away, you can grab the latest distribution from this link.

Just the .js

Example pages

And the complete source code, including unit tests, is available via darcs repository use:

darcs get http://twologic.com/repos/inheritance/

ok