Saying Goodbye To Prototype

Posted by Charlie Fri, 01 Feb 2008 08:22:00 GMT

Maybe its just me, but what I want from a JavaScript library seems to be diverging from what Prototype provides. What I want, in order of importance, is:

  • A cross-browser API that hides some of the major differences between Internet Explorer and standards compliant browsers
  • Unobtrusive
  • As small as possible, and if that's not possible, then at least modular
  • A selector API

Where Prototype really falls down is on points two and three - its very obtrusive, getting larger by the day and isn't modular.

Accepting Something for What It Is

Prototype's greatest sin is its disdain for JavaScript.You can see this disdain shine through in a number of ways.

First, Prototype originated as part of Rails, which provides helpers that use Ruby code to generate JavaScript. If programs could talk, Rails would be saying "Let me take care of this for you since you certainly don't want to dirty your hands with JavaScript."

Second, Prototype wastes over 200 lines of code (about 5%) duplicating Ruby's Enumerable API in JavaScript, for no obvious reason except the developers prefer Ruby's way of doing things. The problem is that Ruby's Enumerable API is based on one of the core features of Ruby - its elegant use of anonymous functions (called blocks) to apply snippets of code to a sequence of items. JavaScript has first-class anonymous functions, but it doesn't have the language support for using them as iterators. As a result, Prototype's JavaScript code doesn't look natural because it is working outside the design strengths of JavaScript. And more importantly, it forces Prototype into using exceptions as a iteration signaling method, which is a nasty hack.

For example, let's look at the any method. In Ruby, any? returns true if an item in a list matches some criteria. Thus to find if any number in an array is odd you would write this:

[2, 4, 6, 8, 11].any? do |value|
  value.even? # even? is from Rails, not Ruby 
end

In my view, porting any to JavaScript is of dubious value at best. But let's look at the contortions that Prototype has to go through to do it:

any: function(iterator, context) {
  iterator = iterator ? iterator.bind(context) : Prototype.K;
  var result = false;
  this.each(function(value, index) {
    if (result = !!iterator(value, index))
    throw $break;
  });
  return result;
}

each: function(iterator, context) {
  var index = 0;
  iterator = iterator.bind(context);
  try {
    this._each(function(value) {
    iterator(value, index++);
  });
  } catch (e) {
    if (e != $break) throw e;
  }
  return this;
}

_each: function(iterator) {
  for (var i = 0, length = this.length; i < length; i++)
  iterator(this[i]);
}

The any method calls each with calls _each which then calls your method. And since JavaScript doesn't support returning values from an anonymous function used as an iterator (there is no yield keyword like in Ruby), the any method is forced to throw an exception (see $break) to signal that an element has been found. That might seem like a small offense until you are trying debug JavaScript code using Venkman and keep interrupted by meaningless exceptions (which happens if you've asked Venkman to stop at all errors and exceptions).

More examples of trying to make JavaScript more like Ruby abound:

  • The addition of a Class object that introduces an initialize function, instead of just accepting JavaScript's combined constructor/initalizer idiom
  • A number of useless additions to the String class (methods like succ, times, etc) - 100 plus lines of code
  • A number of useless additions to the Array class (methods like succ, times, etc) - a bit less than 100 lines of code

The end result is that over 10% of Prototype is wasted trying to add Ruby like-features to JavaScript that don't fit well, simply because the Prototype designers prefer Ruby's idioms over JavaScript's idioms. The obvious problem is that Prototype is a JavaScript library, not a Ruby library.

Lay Off My Prototypes

Prototype also fails miserably on the unobtrusive test. In its first version, Prototype added methods to JavaScript's Object prototype - which is a big no-no. Not learning from its past mistakes, the latest version of Prototype has this gem:

(function() {
  var element = this.Element;
  this.Element = function(tagName, attributes) {
  attributes = attributes || { };
  tagName = tagName.toLowerCase();
  var cache = Element.cache;
  if (Prototype.Browser.IE &amp;&amp; attributes.name) {
    tagName = '<' + tagName + ' name="' + attributes.name + '">';
    delete attributes.name;
    return Element.writeAttribute(document.createElement(tagName), attributes);
  }
  if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
  };
  Object.extend(this.Element, element || { });
}).call(window);

Take a good, long look at this method. It replaces a browser's built in Element object, which is used to represent elements in a DOM tree, with an Element function. Replacing a core browser object is nuts. Especially for the ridiculously small payoff. Instead of writing this:

var element = document.createElement('foo')
element.id = 7

This change lets you write this:

var element = new Element('foo', {id: 7})

And how many times does Prototype use this function? A measly 4 times! And to add insult to injury, the code as written is broken because it breaks the prototype chain. The last line in the function should be:

this.Element.prototype = element.prototype

Without this line, any custom extensions you've made to the Element object are lost. Trust me, it took a good long time to debug why our code no longer worked.

Time for a Diet

Finally, Prototype is getting bigger with every release. Version 1.5 weighs in at 3,396 lines of code while version 1.6 is 4,307 lines, a 27% increase. I'm sure the additional code is useful, but I'm also sure there are great swaths of Prototype that I don't need. Unfortunately, Prototype doesn't provide a mechanism to package up only the parts of it you want. When the library was smaller, that was a reasonable decision. But as Prototype continues to grow, there will come a point where its benefits are outweighed by its weight (and for me I've passed that point).

So What Next

The last few years have been JavaScript's golden years, marked by an amazing outpouring of experimentation and creativity that has led to a number of great JavaScript libraries. A huge benefit of this work is revealing the pain points, beyond cross-browser compatibility issues, of working with JavaScript. These issues include the lack of a Selector API, better iterators, better chaining of DOM methods, wordy method names (getElementById), etc.

Of course each library takes its own approach to solving these problems, and with that comes a downside - lockin. For large JavaScript projects, switching between libraries is a boring, tedious, time-consuming undertaking. Which is the reason we've remained with Prototype for as long as we have and will continue to do so for a bit longer while we plan our migration to a new library.

Posted in  | 15 comments | 1 trackback

A Simple, Lightweight JavaScript Templating Engine

Posted by Charlie Wed, 13 Jun 2007 21:30:00 GMT

Update - Armin has an alternate implementation based on some fancy regular expressions combined with String's split method. Its supports most of ERB and avoids all the string mashing I do. Nice!

Templating engines are the most popular way to generate HTML pages and other web content. First popularized by PHP and ASP , templating engines allow you to mix code and content. The templating engine then takes the combined content, extracts the code, runs it, and combines the results with the remaining content to produce the final output.

Since templating engines are generally used to create HTML that is displayed by a browser, they are almost always run on a server. But now that all modern browsers support the DOM, XML and Ajax, it can be helpful to run a templating engine on the client.

Before continuing, remember that JavaScript templates are often not the right solution. Alternatives include generating HTML on a server, or if you are using XML, to use XSL on the client or server to generate HTML.

But if you need something simple and light, perhaps to display a JSON result returned by an Ajax request, then JavaScript templates may fit the bill.

Writing the Templating Engine

A quick search on the Internet found a few existing engines, such as JavaScript Templates, Ajax Pages and the Prototype library. However, I found the first two to be a bit heavyweight while Prototype was a bit to simple (it only supports the replacement of values, not the execution of arbritrary statements such as for loops). So I decide to roll my own.

Creating a template engine in JavaScript is remarkably easy due to the power of String's replace method. One of its lesser known features is that you specify a function to invoke every time a pattern is matched. The pattern is replaced by the results of the invoked function. Using replace, you can write a template compiler in ten lines of code (and undoubtedly less if you wanted to).

The whole templating engine weighs in at 90 lines, including a helper function copied from the Prototype library. The engine defines two objects - a template object and a parser object. The template object takes a string that includes mixed code and content, invokes the parser to compile the template, and then evaluates and returns the result.

Using the Templating Engine

To see how this works, I've created a simple example that is online. If you look at the HTML code, you'll see:

function replaceContent()
{
  var colorsArray = ['Red', 'Green', 'Blue', 'Orange']
  var source = 
    '<p>Here is a list of <%= this.colors.length %> colors:' + 
    '  <ul>' +
    '    <% for (var i=0; i<this.colors.length; i++) { %>' + 
    '      <li><%= this.colors[i] %></li>' + 
    '    <% } %>' + 
    '	 </ul>' +
    '</p>'
		
   var template =  new JsTemplate.Template(source)
   var content = template.run({colors: colorsArray})

   var element = document.getElementById('content')
   element.innerHTML = content
}

The first thing to notice is that the source variable specifies the mixed code and content. The syntax is similar to ERB, which is a Ruby templating engine. The two recognized tags are:

<% %>   Run JavaScript code
<%= %>  Replace JavaScript code with the result

To create your own tags create a new Parser object with an appropriate regular expression.

The second thing to notice is that the data use by the template engine is specified via a parameter to the run method. The parameter should be a JavaScript object. The properties of the object are copied to the template object, thus allowing the template to refer to them via the this keyword. And that is about it (I said it was lightweight!).

To source code of the templating engine is here, while the example is here. Note the code is released under an MIT license, so you can use it however you would like. Enjoy.

Posted in , ,  | 6 comments | 1 trackback

Opera, Ajax and Bugs

Posted by Charlie Tue, 12 Jun 2007 23:20:00 GMT

Opera 9 is a great browser - it small, standards compliant and fast. And not just slightly faster - really fast (I don't believe Apple's browser speed comparision they've put up with the Safari 3 Beta - its not what I've seen in the real world).

For example, out of the box its ten times faster than Firefox at rendering complex SVG drawings. IIt almost fast enough that creating SVG maps is plausible...ah well...skip that...but that is for another post. Anyway, I'll let you in on a little secret - Firefox's rendering performance is salveagable - go read SVG's suspendRedraw and unsuspendRedrawAll apis..

Hashing up Ajax

However, Opera has one awful gotcha - you cannot return an HTTP status code other than 200 in an Ajax request and get the response body back. You might be thinking that's awfully obscure, but its not.

For example, say your server supports the Atom Publishing Protocol. When a user POSTs a new resource to your server, its job is to return a representation of the new resource with an HTTP status code of 201 CREATED. So something like this:

    HTTP/1.1 201 Created
    Date: Fri, 7 Oct 2005 17:17:11 GMT
    Content-Length: nnn
    Content-Type: application/atom+xml;type=entry;charset="utf-8"
    Location: http://example.org/edit/first-post.atom
    ETag: "c180de84f991g8"  

    <?xml version="1.0"?>
    <entry xmlns="http://www.w3.org/2005/Atom">
      <title>Atom-Powered Robots Run Amok</title>
      <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
      <updated>2003-12-13T18:30:02Z</updated>
      <author><name>John Doe</name></author>
      <content>Some text.</content>
      <link rel="edit"
          href="http://example.org/edit/first-post.atom"/>
    </entry>

Except Opera won't return the result! If you check the XmlHttpRequest's responseText or responseXML attributes they are NULL. This is problematic, because the response contains valuable information - such as the ID the server has assigned the new entry.

It also means you can't use standard error codes if your client needs access to the response body.

The Frustration of it All

What's galling about this bug is that its most likely caused by an if statement deep in the bowls of the Opera that intentionally throws away the result when the status code is not 200! Thus, its probably a simpe fix...which leads to the next frustration.

As good as Opera's browser is, its bug tracking sytem is as bad. I submitted a bug about this over a year ago and obviously it hasn't been fixed. I can live with that, but it sure would be nice to see its current status.

Except Opera hides its bug reports, like many other companies. I've never understood the logic of this. I've heard the argument that a bug report could contains information that could competitively disadvantage a company - but how often does that really happen? And when it does, just delete the information - you do read your bug reports, right!

I think the real reason is that corporations don't like to appear fallible, and bugs are obvious, small failures. However hiding them does not make them go away - instead it just frustrates users. Everyone knows software has bugs - so why not face up to the truth?

So to change the world in my own small way, MapBuzz's bug reports are public and always will be.

And that's the end of today's rant :)

Posted in , ,  | 2 comments | no trackbacks

What Went Wrong with my Javascript?

Posted by Charlie Tue, 08 May 2007 19:06:00 GMT

Now that you've launched your new killer Web 2.0 website, how do you detect errors in your deployed Javascript?

Using onerror

The standard approach is to hook into the Window object's onerror event handler and use Ajax to log your requests to the server. There are some good tutorials on the web on how to do this, but here is one approach:

// Register global error handler
window.onerror = function(message, uri, line)
{
  var fullMessage = message + "\n at " + uri + ": " + line
  remoteLogger.log(fullMessage)
  // Let the browser take it from here
  return false                      
}

Notice that you have to explicity set window.onerror using old-style event handlers, attachEvent and addEventListener don't seem to do the trick. Also, Internet Explorer always stupidly sets the URI parameter to the URI of the current page, not the file that caused the problem. Thus you have to hunt through all the linked Javascript files looking at the specified line number and make a best guess as to which file caused the problem.

Getting a Stack Trace

Often times knowing where an error occurred is not enough - what you really need is the full stack trace. If your user has a Gecko based browser (eg, Firefox), you're in luck.

Notice the stack parameter in the code above? Gecko has a poorly documented stack property on the global Error object that provides just what we need.

Let's say you have code that looks like this:

function first()
{
  second()
}

second = function()
{
  blowUp()
}

function blowUp()
{
  try
  {
    foo.bar()
  }
  catch (e)
  {
    handleException(e)
  }         
}
If you call the first method, say from onload like in this example, the resulting stack trace is:
 blowUp()@file:///C:/temp/error.js:17
                ()@file:///C:/temp/error.js:10 
	   first()@file:///C:/temp/error.js:
	onload([object Event])@file:///C:/temp/error.html:1 
 	          @:0

Each line has the following format:

<method_name>(<arguments>)@<uri>:<line_number>

Notice that the method_name will be blank for anonymous functions, which are quite common if you follow the prototype javascript style.

A Simple Logger

Now let's take a look at some code. First, here is a simple logger that posts errors to a server via Ajax (note this code assumes your are using the prototype library):

RemoteLogger = function RemoteLogger()
{
  this.logging = false
}

Object.extend(RemoteLogger.prototype,
{
  log: function(message, stack)
  {
    if (!this.logging)
    {
      this.logging = true

      var parameters = 'resource=error'
      var postBody = 'error=' + encodeURIComponent(message) + 
                     '&stack=' + encodeURIComponent(stack)

      var requestOptions = 
      {
        method: 'post',
        parameters: parameters,
        postBody: postBody,
        onSuccess: function(transport)
        {
          this.logging = false
        }.bind(this)
      }
    
      new Ajax.Request('/logger', requestOptions)
    }
  }
})

Handling Exceptions

Next, let's define the handleException method we used above. This method extracts out useful information from an Error object and uses the logger above to post the results to a server.

function handleException(exception)
{  
  /* In FF exception can be a string if it happens
     when opening the xmlHttpRequest.  Gah! */
  if (typeof exception == 'string')
    exception = new Error(exception)
  
  /* If a xmlhttp request is happening in Mozilla and
     the user navigates to another page, then when
     the first request returns a NS_ERROR_NOT_AVAILABLE
     error will be thrown.  So just ignore it. */ 
  if (exception.name == 'NS_ERROR_NOT_AVAILABLE') return
      
  var fullMessage = ''
  var uri = ''
  var stack = ''
  var line = ''
      
  try
  {                    
    /* Don't use exception.toString since the JS spec
       does not require it to provide the error name or message
       (haven't tested to see if it matters though across browsers) */
    fullMessage = exception.name + ': ' + exception.message
    
    uri = exception.fileName
    stack = exception.stack
    
    // Firefox sometimes blows up here
    line = exception.lineNumber
  }
  catch (e)
  {
  }                  

  fullMessage += "\n at " + uri + ": " + line
  console.info(fullMessage)
  console.info(stack)
  remoteLogger.log(fullMessage, stack)      
}

Annoying Gotchas

There are few annoying gotchas to know about:

  • Stack traces are only available from Error objects thrown by exceptions and thus are not available from the onerror method.
  • Uncaught exceptions are not hanlded by onerror.
  • There is no global exception handler in Javascript, so you have to be very careful in the way you right your code. On the positive side, its easy to implement a global error handler for methods that are invoked due to the results of an Ajax request. On the other hand, its much harder to do this for methods invoked due to normal events generated by a user interacting with the browser.

Anyway, the more information you can get about browser errors the better - you'll often be surprised by the results!

Posted in , ,  | no comments | no trackbacks

New and Improved JavaScript Inheritance

Posted by Charlie Fri, 25 Aug 2006 21:48:00 GMT

Last time I talked about my implementation of JavaScript inheritance. Since then I've made a number of improvements - its now simpler, faster and easier to use.

In my post I pointed out some alternative, including one from Kevin Lindsey. Kevin's implementation is my favorite because its the simplest and least intrusive solution.

It turns out our two implementation are quite similar - the main difference is that I added in a a callSuper method. This might not seem like much, but let's look at an example to see how is dramatically changes the code you write.

Let's assume we have a Pet object that inherits from a Animal object (I don't use "class" since JavaScript doesn't have classes, just prototypes).

Here is how you would code in using Kevin's implementation:

function Pet(owner, name)
{
  this.owner = owner
	 Pet.baseConstructor.call(this, owner, name)
}
KevLinDev.extend(Pet, Animal)

Pet.prototype.toString = function()
{
  return Pet.superClass.toString.call(this) + "\n" + 
         "My owner is " + this.owner
}

Notice three things. First, the name of the current class is hard-coded into methods (for example, look at Pet.baseConstructor). Second, calling an overridden constructor (Pet.baseConstructor for example) is different than calling an overridden method (Pet.superClass). Last, invoking a super method requires using JavaScript's call function instead of a normal method call.

Now let's add in a callSuper method and see what the code looks like:

function Pet(owner, name)
{
  this.owner = owner
  this.callSuper(arguments.callee, name)
}
Pet.inherits(Animal)

Pet.prototype.toString = function()
{
  return this.callSuper(arguments.callee) + "\n" + 
         "My owner is " + this.owner
}

We've gotten rid of all the limitations mentioned above - but have added two.

This first is the need to pass arguments.callee as the first parameter. This is used by callSuper to determine the currently executing method. Without this information, callSuper cannot lookup the overridden super method.

In previous versions of JavaScript you don't need this parameter because you could determine what method invoked a method by arguments.callee.caller. However, caller has been deprecated in JavaScript. Thus this works in Firefox and Internet Explorer but not Safari, Konqueror, and if I remember correctly, Opera.

The second limitation is hidden - using callSuper introduces a performance hit since it has to look up the calling method (a consequence of caller being deprecated). However, in my new improved implementation this performance hit has been eliminated for some common cases and in other cases it has been reduced by 50%. Thus it only comes into play if you're creating hundreds and hundreds of objects.

Updated Implementation

So let's look at the new implementation:

/* Copyright (c) 2006 Charlie Savage 
License: BSD:
http://www.opensource.org/licenses/bsd-license.php
*/

Function.prototype.inherits = function(superConstructor)
{
  /* Borrowed from Kevin Lindsey - create a dummy
     constructor to create prototypes.  This is useful
     because it means that we don't have to modify
     the real constructors to have if-statements to 
     guard against initialization if we are creating
     a prototype (versus a regular instance).*/
  function CreatePrototype() {}
  CreatePrototype.prototype = superConstructor.prototype
  
  /* Reset the current constructor. This is an ugly hack required 
     by the JavaScript prototype-chain implementation. */
  this.prototype = new CreatePrototype()
  this.prototype.constructor = this
  
  // Save a reference to the superClass constructor
  this.superConstructor = superConstructor

  this.prototype.callSuper = function()
  {
    /* In IE and Firefox we can get the caller argument via
       arguments.callee.caller.  However, for some reason this
       has been deprecated in JavaScript and is not supported
       by Safari or Opera.  So we have to pass it in as
       a parameter.  Yuck.*/
    // var caller = arguments.callee.caller
    var caller = arguments[0]
    var args = new Array()
    for (var i=1; i<arguments.length; i++)
      args.push(arguments[i])
    
     /* Figure out where we are in the inheritance hierarchy and
       the name of the method that was invoked. */
    var currentConstructor = this.constructor
    var methodName = null 
    
    while (methodName == null && currentConstructor != null)
    {
      // Shortcut - Is a constructor being called?
      if (caller === currentConstructor)
        return currentConstructor.superConstructor.apply(this, args)
  
      methodName = figureMethodName(currentConstructor.prototype, caller)
      currentConstructor = currentConstructor.superConstructor
    }
    
    if (!methodName)
      throw new Error("Could not find method: " + methodName + ".")

    if (!currentConstructor)
      throw new Error("Prototype does not have an ancestor: "
       + currentConstructor + ".")
      
    // Finally - execute the method     
    return currentConstructor.prototype[methodName].apply(this, args)
  }    
}

function figureMethodName(prototype, method)
{
  for (var key in prototype)
  {
    if (prototype.hasOwnProperty(key) &&
        prototype[key] === method)
      return key
  }
  return null
}

And here is an example of using it:

// -- Animal --
function Animal(name)
{
  if (!name)
    throw new Error('Must specify an animal name')
  this.name = name
}

Animal.prototype.toString = function()
{
  return 'My name is ' + this.name
}

// -- Pet --
function Pet(owner, name)
{
  this.owner = owner
  this.callSuper(arguments.callee, name)
}
Pet.inherits(Animal)

Pet.prototype.toString = function()
{
  return this.callSuper(arguments.callee) + "\n" + 
         "My owner is " + this.owner
}

// -- Cat --
function Cat(owner, name)
{
  this.callSuper(arguments.callee, owner, name)
}
Cat.inherits(Pet)
 
Cat.prototype.toString = function()
{
  return this.callSuper(arguments.callee) + '\n' + 
        'I eat mice'
}
        
var cat = new Cat('charlie', 'oba')
alert(cat.toString())

CreatePrototype

The first thing that might look suspicious is CreatePrototype. This is a brilliant idea I borrowed from Kevin. It neatly solves a common problem that bites all new JavaScript developers - and sometimes experienced ones who are forgetfully like yours truly!

Take a look at the Animal constructor:

function Animal(name)
{
  if (!name)
    throw new Error('Must specify an animal name')
  this.name = name
}

In traditional JavaScript inheritance, to have Pet inherit from Animal, you'd write the following code:

Pet.prototype = new Animal()
Pet.prototype.constructor = Pet

When we try to create a new Animal as a prototype, the Animal constructor will throw an exception because it was not passed a name. To avoid this, you have to sprinkle ugly if-statements throughout your constructors to protect code that should only be called when creating a new instance versus prototypes.

CreatePrototype solves the whole mess because it entirely skips calling the Animal constructor. Instead, new CreatePrototype() creates a blank new object. However, that object's __proto__ property is set to the Animal's prototype thereby inheriting all its methods which is exactly what we want.

Let's look at a diagram to see what this looks like:

JavaScript inheritance

The gray boxes with capital letters are constructors (JavaScript functions) while the blue objects with underlined letters are instance of objects.

By using CreatePrototype(), we replace animal with createPrototype which is nothing more than a blank object. But notice that the __proto__ properties are still correctly setup.

callSuper

So how does callSuper work?

First, we need to determine the name of the method being currently executed and what prototype contains it. Let's use Cat.toString as an example - its defined as an anonymous function so it doesn't actually have a name. Thus the need for the ugly callee parameter, which is pointer to the currently executing method:

Cat.prototype.toString = function()
{
  return this.callSuper(arguments.callee) + "\n" + "I eat mice"
}

Starting at the Cat prototype we climb up the inheritance tree until we find the calling method. To do this, we call figureMethodName for each prototype, specifying the current prototype and method (i.e., caller).

function figureMethodName(prototype, method)
{
  for (var key in prototype)
  {
    if (prototype.hasOwnProperty(key) &&
        prototype[key] === method)
      return key
  }
  return null
}

The call to hasOwnProperty is quite important - we only want to return methods defined on the current prototype. If we return a method defined by a super class, then we'll end up with infinite recursion. That happens because we'll end up calling the method on a child prototype instead of the super prototype. Thus, when callSuper is invoked again we'll end up back in the same place (remember that each time we invoke callSuper we start at the bottom of the inheritance chain because this always refers to the current JavaScript object).

Once we've found the method name, then we simply invoke it on the superConstructor and let JavaScript do the rest.

Here is the updated code - and as usual all feedback is welcome.

Posted in  | no comments | no trackbacks

New and Improved JavaScript Inheritance

Posted by Charlie Fri, 25 Aug 2006 21:48:00 GMT

Last time I talked about my implementation of JavaScript inheritance. Since then I've made a number of improvements - its now simpler, faster and easier to use.

In my post I pointed out some alternative, including one from Kevin Lindsey. Kevin's implementation is my favorite because its the simplest and least intrusive solution.

It turns out our two implementation are quite similar - the main difference is that I added in a a callSuper method. This might not seem like much, but let's look at an example to see how is dramatically changes the code you write.

Let's assume we have a Pet object that inherits from a Animal object (I don't use "class" since JavaScript doesn't have classes, just prototypes).

Here is how you would code in using Kevin's implementation:

function Pet(owner, name)
{
  this.owner = owner
	 Pet.baseConstructor.call(this, owner, name)
}
KevLinDev.extend(Pet, Animal)

Pet.prototype.toString = function()
{
  return Pet.superClass.toString.call(this) + "\n" + 
         "My owner is " + this.owner
}

Notice three things. First, the name of the current class is hard-coded into methods (for example, look at Pet.baseConstructor). Second, calling an overridden constructor (Pet.baseConstructor for example) is different than calling an overridden method (Pet.superClass). Last, invoking a super method requires using JavaScript's call function instead of a normal method call.

Now let's add in a callSuper method and see what the code looks like:

function Pet(owner, name)
{
  this.owner = owner
  this.callSuper(arguments.callee, name)
}
Pet.inherits(Animal)

Pet.prototype.toString = function()
{
  return this.callSuper(arguments.callee) + "\n" + 
         "My owner is " + this.owner
}

We've gotten rid of all the limitations mentioned above - but have added two.

This first is the need to pass arguments.callee as the first parameter. This is used by callSuper to determine the currently executing method. Without this information, callSuper cannot lookup the overridden super method.

In previous versions of JavaScript you don't need this parameter because you could determine what method invoked a method by arguments.callee.caller. However, caller has been deprecated in JavaScript. Thus this works in Firefox and Internet Explorer but not Safari, Konqueror, and if I remember correctly, Opera.

The second limitation is hidden - using callSuper introduces a performance hit since it has to look up the calling method (a consequence of caller being deprecated). However, in my new improved implementation this performance hit has been eliminated for some common cases and in other cases it has been reduced by 50%. Thus it only comes into play if you're creating hundreds and hundreds of objects.

Updated Implementation

So let's look at the new implementation:

/* Copyright (c) 2006 Charlie Savage 
License: BSD:
http://www.opensource.org/licenses/bsd-license.php
*/

Function.prototype.inherits = function(superConstructor)
{
  /* Borrowed from Kevin Lindsey - create a dummy
     constructor to create prototypes.  This is useful
     because it means that we don't have to modify
     the real constructors to have if-statements to 
     guard against initialization if we are creating
     a prototype (versus a regular instance).*/
  function CreatePrototype() {}
  CreatePrototype.prototype = superConstructor.prototype
  
  /* Reset the current constructor. This is an ugly hack required 
     by the JavaScript prototype-chain implementation. */
  this.prototype = new CreatePrototype()
  this.prototype.constructor = this
  
  // Save a reference to the superClass constructor
  this.superConstructor = superConstructor

  this.prototype.callSuper = function()
  {
    /* In IE and Firefox we can get the caller argument via
       arguments.callee.caller.  However, for some reason this
       has been deprecated in JavaScript and is not supported
       by Safari or Opera.  So we have to pass it in as
       a parameter.  Yuck.*/
    // var caller = arguments.callee.caller
    var caller = arguments[0]
    var args = new Array()
    for (var i=1; i<arguments.length; i++)
      args.push(arguments[i])
    
     /* Figure out where we are in the inheritance hierarchy and
       the name of the method that was invoked. */
    var currentConstructor = this.constructor
    var methodName = null 
    
    while (methodName == null && currentConstructor != null)
    {
      // Shortcut - Is a constructor being called?
      if (caller === currentConstructor)
        return currentConstructor.superConstructor.apply(this, args)
  
      methodName = figureMethodName(currentConstructor.prototype, caller)
      currentConstructor = currentConstructor.superConstructor
    }
    
    if (!methodName)
      throw new Error("Could not find method: " + methodName + ".")

    if (!currentConstructor)
      throw new Error("Prototype does not have an ancestor: "
       + currentConstructor + ".")
      
    // Finally - execute the method     
    return currentConstructor.prototype[methodName].apply(this, args)
  }    
}

function figureMethodName(prototype, method)
{
  for (var key in prototype)
  {
    if (prototype.hasOwnProperty(key) &&
        prototype[key] === method)
      return key
  }
  return null
}

And here is an example of using it:

// -- Animal --
function Animal(name)
{
  if (!name)
    throw new Error('Must specify an animal name')
  this.name = name
}

Animal.prototype.toString = function()
{
  return 'My name is ' + this.name
}

// -- Pet --
function Pet(owner, name)
{
  this.owner = owner
  this.callSuper(arguments.callee, name)
}
Pet.inherits(Animal)

Pet.prototype.toString = function()
{
  return this.callSuper(arguments.callee) + "\n" + 
         "My owner is " + this.owner
}

// -- Cat --
function Cat(owner, name)
{
  this.callSuper(arguments.callee, owner, name)
}
Cat.inherits(Pet)
 
Cat.prototype.toString = function()
{
  return this.callSuper(arguments.callee) + '\n' + 
        'I eat mice'
}
        
var cat = new Cat('charlie', 'oba')
alert(cat.toString())

CreatePrototype

The first thing that might look suspicious is CreatePrototype. This is a brilliant idea I borrowed from Kevin. It neatly solves a common problem that bites all new JavaScript developers - and sometimes experienced ones who are forgetfully like yours truly!

Take a look at the Animal constructor:

function Animal(name)
{
  if (!name)
    throw new Error('Must specify an animal name')
  this.name = name
}

In traditional JavaScript inheritance, to have Pet inherit from Animal, you'd write the following code:

Pet.prototype = new Animal()
Pet.prototype.constructor = Pet

When we try to create a new Animal as a prototype, the Animal constructor will throw an exception because it was not passed a name. To avoid this, you have to sprinkle ugly if-statements throughout your constructors to protect code that should only be called when creating a new instance versus prototypes.

CreatePrototype solves the whole mess because it entirely skips calling the Animal constructor. Instead, new CreatePrototype() creates a blank new object. However, that object's __proto__ property is set to the Animal's prototype thereby inheriting all its methods which is exactly what we want.

Let's look at a diagram to see what this looks like:

JavaScript inheritance

The gray boxes with capital letters are constructors (JavaScript functions) while the blue objects with underlined letters are instance of objects.

By using CreatePrototype(), we replace animal with createPrototype which is nothing more than a blank object. But notice that the __proto__ properties are still correctly setup.

callSuper

So how does callSuper work?

First, we need to determine the name of the method being currently executed and what prototype contains it. Let's use Cat.toString as an example - its defined as an anonymous function so it doesn't actually have a name. Thus the need for the ugly callee parameter, which is pointer to the currently executing method:

Cat.prototype.toString = function()
{
  return this.callSuper(arguments.callee) + "\n" + "I eat mice"
}

Starting at the Cat prototype we climb up the inheritance tree until we find the calling method. To do this, we call figureMethodName for each prototype, specifying the current prototype and method (i.e., caller).

function figureMethodName(prototype, method)
{
  for (var key in prototype)
  {
    if (prototype.hasOwnProperty(key) &&
        prototype[key] === method)
      return key
  }
  return null
}

The call to hasOwnProperty is quite important - we only want to return methods defined on the current prototype. If we return a method defined by a super class, then we'll end up with infinite recursion. That happens because we'll end up calling the method on a child prototype instead of the super prototype. Thus, when callSuper is invoked again we'll end up back in the same place (remember that each time we invoke callSuper we start at the bottom of the inheritance chain because this always refers to the current JavaScript object).

Once we've found the method name, then we simply invoke it on the superConstructor and let JavaScript do the rest.

Here is the updated code - and as usual all feedback is welcome.

Posted in  | no comments | no trackbacks

Yet Another JavaScript Inheritance Implementation

Posted by Charlie Tue, 22 Aug 2006 05:27:00 GMT

This code has been deprecated - use the new and improved version instead.

I've always been a fan of JavaScript - its flexibility makes it well tailored for its primary execution environment inside of web browsers. It certainly counts as a language that gets out of your way.

As you probably know, JavaScript is based on prototypes. What that means in practice is that you can change all existing instances of an object at runtime, after they've been created, by adding or removing methods from the object's prototype. You can also update a particular instance of an object at runtime by manipulating just that object.

This capability is by no mean unique to JavaScript - Ruby let's you do the same thing for example - but JavaScript is the most widely deployed language that has this sort of flexibility. And I believe its the only widely deployed prototype based language (anyone know of another that is widely deployed?).

JavaScript Inheritance

Although JavaScript is fully object-oriented, its inheritance mechanism is clunky. In fact, until the Ajax craze most people didn't even know that JavaScript supported inheritance. Just look at the prototype library - it defines an extend method that copies all the properties/method from one object to another. Its a great example of JavaScript's flexibility, but not a great example of software engineering.

The JavaScript 1.5 guide is the place to learn how JavaScript's inheritance works. Its an updated version of an old Netscape DevEdge article that the Mozilla Foundation rescued from oblivion. I still remember reading it back in 2000 - it was quite an eye opener - I hadn't realized the power hiding within JavaScript.

Over the years a myriad of implementations have popped up to simplify JavaScript inheritance. Some of the well known ones are from Douglas Crockford, Kevin Lindsey, Joshua Gertzen and Dean Edwards. These are all fine implementations. They of course differ somewhat in their capabilities and complexity.

My Take

A while back I put together my own implementation, but never published it. Since my JavaScript posts seem to be pretty popular, I thought it was time to open it up for review. Feel free to use it if you'd like.

I had two main goals in my implementation:

  • Keep it as close to "stan" JavaScript as possible
  • Support multiple levels of inheritance

Its the first goal that distinguishes this implementation from others. In Douglas' approach you have to use a specific API to define methods, while in Joshua's and Dean's approaches you have to inherit from a base class and use a specific API to define methods. For me Kevin's approach is the cleanest, but I didn't come across it until after I had put together my own implementation.

Here is an example of how it looks (a quick aside - in this example I use the old-school way of defining prototype methods to keep things simple. In real-code, I generally use the Object.extend idiom popularized by the Prototype library to add methods to an object - but not to "" inheritance):

// -- Animal --
function Animal(name)
{
  this.name = name

}

Animal.prototype.toString = function()
{
  return "My name is " + this.name

}

// -- Pet --
function Pet(owner, name)
{

  this.owner = owner
  this.callInherited(arguments.callee, name