27.11.06

Metaprogramming in JavaScript

Several months ago, I was working on an interface for form elements. The idea was to create an API for easy manipulation, since our old API was growing out of control and it was hard to use.

Compared to the old one, this new API was object oriented, with methods like "validate", "initialize" and "clone". Experience taught us that there would be a lot more than these methods, so a structure was needed that would allow the API to be maintainable and scalable.

First thing to do was to transpose the approach to the common OO API (the justification is beyond the scope of this post). Methods would not be piled together under one class (or input type), but the other way around: each method would be a "class" and each input would be a "method". This prevented a problem we had in the past: someone added new functionality to text and textarea, but didn't bother doing it for date. (these are not regular HTML inputs but inputs as a user perceives them)

The next issue was to make the API load effortlessly, without having to add the definition into a massive, unreadable file or redundantly in more than one. One of the reasons things are not maintained well is because it's not easy to maintain them. With this in mind, we had to ensure that developers would have a minimum amount of code to write to add the desired functionality. This is where metaprogramming steps in.

Metaprogramming, as defined on a piece of paper tucked away in a shirt pocket that I forgot to check before chucking it into the washer, is a technique that allows a program to spew programs. In other words, this code will create code. Specifically, the methods will be defined separately in files and our framework will suck them up and construct an API. Automatically.

This is nothing new to Java, for example. You create a Java class and it's magically picked up and read from some path. Well, I wanted that simplicity in my JavaScript, so I promptly whipped:
function generateInterface(obj) {
// This is an external XHR function that calls the
// server and returns an array of file names
// in a given path, for example ('/api')
// returns: ['/api/clone.js', '/api/validate.js',
// '/api/init.js']

var definitions = loadUrl(path);

// Iterating through the paths to get the name of the file
for (var i, url=definitions[i=0]; url; url=definitions[i++]) {
var method = url.match(/\/([\w]+)\.js$/)[1];

// Note the anonymous function to avoid calling the

// wrong function at runtime
(function (method) {
obj[method] = function () { route(method); };
})(method);
}
return obj;
}
Ok, so now I just have to write the route function, which gets the source from the paths, caches it for subsequent calls and calls the appropriate methods. Oh, I also have to write the code that will return me all the files in a particular path...

Hold on.

Does this seem complex to you? It certainly does to me. Why try to replicate something that exists in another language? Is this language really that weak? A typical blunder among Java/JavaScript programmers is to try to replicate Java functionality in JavaScript.

Well, that's just great. All this yadda-yadda for nothing. How should it be done, then? Include your files with regular <script src=""></script> tags and use prototypes to tie your functions to the appropriate object.

Now, I know you might feel crossed because I made you go through all this code for nothing, but that's not true. The metaprogramming example is still a good one, it's just the justification what sucks. However, I hope my three readers understand that if they whip up some JavaScript and it seems a bit too verbose, there's probably something wrong with it. Pay special attention to code that doesn't do "DHTML", "DOM manipulation" or something like that. Controller logic must remain in the controller, that is, on the server in Java, Perl, PHP or whatever you're using back there.

There's a simple reason to stick to simplicity: maintaining all that code in JavaScript will be your demise.


1 comment:

Avromi said...

maybe you can help me please - thanks

my kids are trying to play some games on toshiba game console and they are getting an error that ..... is null or not an object" i already deleted cookies and temp folder and restarted. can you help me please?

thanks