Ruby's Open Classes and Inheritance in JavaScript

Using the compact extend function to simulate class-based inheritance in JavaScript we have many of the same features of Ruby classes. One important feature is that classes remain open and that changes in the superclass are automatically also in the subclass. Through the extend function's natural chaining of prototypes we can have this open and inheritable changes. Below are the Ruby and JavaScript versions of the same example to show this at work.

Ruby

class Person
  def initialize(first, last)
    @first = first
    @last = last
  end

  def to_s
    @first + ' ' + @last
  end
end

class Employee < Person
  def initialize(first, last, id)
    super(first, last)
    @id = id
  end

  def to_s
    super + ': ' + @id.to_s
  end
end

peter = Employee.new('Peter', 'Michaux', 3)

puts peter # Peter Michaux: 3

# open the Person class and add a new method
class Person
  def reverse_name
    @last + ', ' + @first
  end
end

puts peter.reverse_name  # Michaux, Peter

JavaScript

var Class = {
  extend: function(subclass, superclass) {
            function D() {}
            D.prototype = superclass.prototype;
            subclass.prototype = new D();
            subclass.prototype.constructor = subclass;
            subclass.superclass = superclass;
            subclass.superproto = superclass.prototype;
          }
};

function Person(first, last) {
  this.first = first;
  this.last = last;
}

Person.prototype.toString = function() {
  return this.first + ' ' + this.last;
};

function Employee(first, last, id) {
  Employee.superclass.call(this, first, last);
  this.id = id;
}
Class.extend(Employee, Person);

Employee.prototype.toString = function() {
  return Employee.superproto.toString.call(this) + ': ' + this.id;
};

var peter = new Employee('Peter', 'Michaux', 3);

document.write(peter.toString()); // Peter Michaux: 3

// open the Person class and add a new method
Person.prototype.reverse_name = function() {
  return this.last + ', ' + this.first;
};

document.write(peter.reverse_name()); // Michaux, Peter

And now we have all of this nice Ruby-type behavior with only the same simple eight lines of code that gave us the ability to simulate Ruby's "super". This is just another strength of the extend function.

Although Prototype.js itself doesn't need class-based inheritance, part of that libraries goal is to somewhat simulate Ruby in JavaScript. By included the extend function Prototype.js users could have Ruby-style classes enabled through a very simple JavaScript function.

Comments

Have something to write? Comment on this article.