Welcome!

Coach Wei

Subscribe to Coach Wei: eMailAlertsEmail Alerts
Get Coach Wei via: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Related Topics: RIA Developer's Journal

RIA & Ajax: Article

AJAX Coding - Is This a Little Too Much Hack for Prototype.js?

Prototype.js adds methods to built-in JavaScript objects - but there's nothing wrong per se with that

Coach Wei's Enterprise Web 2.0 Blog

Prototype.js is a popular Ajax toolkit for web developers. I have enjoyed using it despite the complains I heard from people about how Prototype.js does too much JavaScript hacking that breaks other people's code. One of the common one complains is that Prototype.js adds methods to built-in JavaScript objects (such as String object). I tend to brush such complains aside - "well, there is nothing wrong per se by adding some methods to JavaScript objects via standard permitted means".

But this recent incident with my adoption of the latest and greatest Prototype.js 1.6 really made me wonder: is this a little too much hack for Prototype.js? Or is it just bad coding practice?

I built a little application using Prototype and Scriptaculous. The application was fairly straightforward and Prototype+Scriptaculous made building such an application fairly easy too. Well, then. I used a JavaScript compression tool to compress the JavaScript files. The tool I used is Dojo ShrinkSafe. I have used ShrinkSafe a few thousand times and it never disappointed me. Well, this time, with Prototype.js, the shrinked result disappointed me, because it didn't work. Here is what I got when trying to run the application on IE:

The code that produced this error is here (compressed code):

    function(_133,url,_135){
    _133(_135);
    this.transport=Ajax.getTransport();
    this.request(url);
    }

The error is in line "_133(_135): Microsoft JScript runtime error: Function expected", which means that _133 is not a function. This compressed code corresponds to the following original code:

Ajax.Request = Class.create(Ajax.Base, {
   _complete: false,
     initialize: function($super, url, options) {
       $super(options);
       this.transport = Ajax.getTransport();
       this.request(url);
     },
   ....

So this method "initialize" is a method in "Ajax.Request" class, which extends a class called "Ajax.Base" using the class creation mechanism provided by Prototype.js. The error is in line "$super(options)". It says the argument value to parameter "$super" is not a function. So this means the caller did not pass the correct argument to this method? The code that called the above code is here(compressed code):

    return new Ajax.Request(_2,_3);}

Obviously this is a little hard to decode. But If I map the compressed code back to the original code:

    return new Ajax.Request(action, options);

As I looked into Prototype.js source code, I just don't see the first argument passed into this method call being a function at all. The "action" argument in the above code is actually a URL string. In fact, there is no place in Prototype.js that passes a function argument in calling Ajax.Request method. Since the "initialize" function itself is expecting the first argument being a function, this is fairly puzzling.

So maybe the Class creation mechanism in Prototype.js itself is doing some magic here? Well, this method looks suspetious:

Class.Methods = {
   addMethods: function(source) {
     var ancestor = this.superclass && this.superclass.prototype;
     var properties = Object.keys(source);
     if (!Object.keys({ toString: true }).length)
       properties.push("toString", "valueOf");
     for (var i = 0, length = properties.length; i < length; i++) {
       var property = properties[i], value = source[property];
       if (ancestor && Object.isFunction(value) &&
           value.argumentNames().first() == "$super") {
         var method = value, value = Object.extend((function(m) {
           return function() { return ancestor[m].apply(this, arguments) };
         })(property).wrap(method), {
           valueOf: function() { return method },
           toString: function() { return method.toString() }
         });
       }
       this.prototype[property] = value;
     }
     return this;
   }
};

Aaaaaahhhhhaaaa! Here we go. When creating a class by extending another class, Prototype.js actually checks the function definitions in the code that defines this new class. If the first argument to a function is named "$super", it does some magic here by automatically creating a function to pass onto this method. This explains why the first argument to "Ajax.Request" is not a function at all, but Prototype.js has taken care of this automatically during the process of defining the "Ajax.request" class. It looked at the source code for function "initialize", saw the first argument called "$super", and automatically created a function internally for it already.

Well, this is clearly a clever hack. It works and it is obviously permitted by the JavaScript language. Yes, in JavaScript, you can obtain the source code of a function definition and do some magic by inspecting the source code. For example, you can trigger some mission critical processing if the 3rd and 4th words of a certain function's source code is "Easter Egg".

However, isn't this a little too much hacking? Doing some special processing conditioned upon how some function argument is named in the source code? What if I just happen to name the first argument of a function "$super", without any intention to have any special magic happening? What if a developer somehow decided to name the first argument to method "initialize" as shown in the above to "method" instead of "$super"? What if some day JavaScript is actually running in a compiled fashion (which means that variable names are not necessarily preserved in its source code form)? No wonder Dojo ShrinkSafe didn't work here because ShrinkSafe renames function arguments to shorter names. -Well, ShrinkSafe made a reasonable assumption - who would expect someone to write code conditioned upon how a variable is named in the source code?

Isn't this just a little too much hacking, Prototype.js? Well, I could be wrong, but I couldn't help but feel exactly how a nervous mother would react to a "look, Ma, no hands" kid dangling on a four floor balcony rail.

More Stories By Coach Wei

Coach Wei is founder and CEO of Yottaa, a web performance optimization company. He is also founder and Chairman of Nexaweb, an enterprise application modernization software company. Coding, running, magic, robot, big data, speed...are among his favorite list of things (not necessarily in that order. His coding capability is really at PowerPoint level right now). Caffeine, doing something entrepreneurial and getting out of sleeping are three reasons that he gets up in the morning and gets really excited.

Comments (1) View Comments

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


Most Recent Comments
Roman 12/27/07 09:02:56 PM EST

Have you reported this to the Prototype developers?