A pretty good presentation from Adam McCrea already explains how to use meta-programming to build your own Javascript libraries. However I’d argue that it’s more about clever APIs than strictly meta-programming. For the best or the worst, meta-programming involves magic. Like dynamic finders in Rails or should() and have() in RSpec.  It’s mostly about dynamically catching method calls, adding methods on the fly or proxying them. Like reprogramming while executing. Hence the name.

So I’m going to show you some techniques available in Javascript to achieve the same results. I’ll start with fairly trivial stuff to move into the nitty-gritty details. Be aware that it’s very easy to shoot yourself in the foot if you abuse these tricks, guaranteeing endless hours of pain for you and your users. You know, with great powers come great responsibilities and such…

A last warning. Some of the techniques are simple plain Javascript but most of them use features only available in SpiderMonkey and Rhino. So if you’re thinking browser, forget about IE  and most non-Mozilla stuff. But I’m thinking server anyway. The good news is that ECMAScript 4 (aka Javascript 2.0) will include some variations of these as part of the standard language (which doesn’t mean the Redmond people will give a shit).

Altering Built-in Prototypes

Javascript is prototype based, so if you want to alter the behavior of all objects of a given “type” you have to alter its prototype. However it’s possible to alter the built-in prototypes in any Javascript engine and make them do whatever suits your fancy. A fairly common way to use this is to add utility functions that make your code nicer, like array.first() for example. Another far more dangerous way is to alter the behavior of existing functions. Needless to say, it’s fairly easy to break a lot of code this way.

For example, imagine we’d like all string concatenation to be separated by a dash:

js> var oldConcat = String.prototype.concat
js> String.prototype.concat = function(other) { return oldConcat.call(oldConcat.call(this, “-“), other); }
js> “aa”.concat(“bb”)
aa-bb

js> String.prototype.concat = oldConcat;
js> “aa”.concat(“bb”)
aabb

As demonstrated above, cleaning up your mess by restoring the original function is highly recommended.

Accessors

A set of methods have been defined in SpiderMonkey and Rhino to let you mess with accessors. These are the sweetly named __defineGetter__, __defineSetter__, __lookupGetter__ and __lookupSetter__. That’s a lot of underscores. The idea is you can get an object, override one of its accessors with a function of yours and do all sort of neat things with it, like proxying calls for example. Here is an example that will let you do just that:

var watchableAttr = function(obj, attr, onSet, onGet) {
if (!obj.__lookupGetter__(attr)) {
var value = obj[attr];
var valueChanged = false;
obj.__defineSetter__(attr, function(v) {
valueChanged = true;
if (onSet) value = onSet(obj, attr, v);
else value = v;
});
obj.__defineGetter__(attr, function() {
if (onGet) return onGet(obj, attr, value);
else return value;
});
obj.__defineGetter__(attr + “Changed”, function() {
return valueChanged;
});
}
};

This function will watch property changes, setting valueChanged to true if it did change. Additionally, if functions are provided for the onSet and onGet attributes, these will get invoked to eventually intercept the call and change its result.

There’s a little gotcha to be aware of here, it’s not directly related to meta-programming but is also interesting. This function call will create three closures, referencing the value and the valueChanged local variables. So when the setter is invoked, the variable that will be set is value, not the underlying accessor. Similarly, the getter will return value, not what the accessor contains. Why doing it that way? Simply because using the accessor directly would result in an endless loop.

One more little detail. These accessor methods also work for array indexes. After all, an array is just an object whose properties are numbers. So if you really want an array to have a constant value at the 0 position:

js> arr.__defineGetter__(0, function() { return 0; });
js> arr[0] = 5;
5
js> arr[0]
0

No Missed Calls

In Ruby, magic is very often implemented with method_missing. It’s a method that’s used as a callback by the interpreter when it can’t find a method being invoked on an object. Add a method_missing method to your classes and all of a sudden you can intercept any missed call.

It turns out that Rhino and SpiderMonkey also have their own  callback method: __noSuchMethod__. Let’s see a trivial example:

js> var myObj = { __noSuchMethod__: function(name, params) { print(“Function ” + name + ” has been called.”); } }
js> myObj.foo();
Function foo has been called.

Neat, isn’t it? Let’s see how a Rails style dynamic finder could be implemented.

this.__noSuchMethod__ = dbCall(function(name, params) {
if (name.startsWith(“findBy”)) {
var criteria = ju.camelAndToArray(name.substring(6));
var stmt = “SELECT * FROM ” + jh.quote(ju.underscore(this.name)) + ” WHERE ” + jh.toParamsNameEq(criteria);
return buildObjects.call(this, jo.db.query(stmt, params));
} else {
throw new ReferenceError(“”” + name + “” is not defined.”);
}
});
}();

There are quite a few utility methods used here but their code isn’t really needed to understand the example. The name of the function being called is checked to see if it starts with “findBy”. Whatever comes after is used as parameters to generate a query dynamically.  The most important part of this code is actually the exception thrown. When you’re playing with that sort of tricks, it’s very important to handle everything you can handle properly and fail as soon as something goes through the cracks. Otherwise you’re up for looong debugging sessions.

This code could also be further optimized. Always going through the __noSuchMethod__ callback has a performance penalty. But if findByName is called once, you can safely assume that it’s going to be called at least a few more times. A better implementation would add the findByName method in the called object so that any subsequent call would find its destination directly. Yep, you can also do that in Javascript.

Conclusion

I’ve detailed the 3 main techniques of meta-programming I’ve seen used in libraries in the wild. The magic bit. Javascript is already a very dynamic language, allowing a lot. But sometimes, having the possibility to do this one more thing makes all the difference between a good framework (JUnit) and a wonderful framework (RSpec). Some would call them hacks. I call them powerful tools that I keep in a special place in my toolbox and try to use wisely.