JavaScript on Rails

With the rise in popularity of the Ruby on Rails framework it was only a matter of time before someone tried to implement it using JavaScript. Of course, when that someone happens to work at Google then all hell breaks loose. Here’s Steve Yegge’s reply to all the hype, which also hints at the evolution of Rhino, which is essentially JavaScript for Java. If you’re not familiar with Rhino, check out what Steve has to say about it:

Rhino…was written with an eye for performance. The Rhino code base reads almost like C code: it avoids allocation and does as much as possible with jump tables to avoid the overhead of virtual method lookups. It has two code paths: a bytecode interpreter that runs in a tight loop, and an optimizing Java bytecode compiler that turns many expensive-ish JavaScript property lookups into Java local or instance-variable lookups. It’s a pretty serious piece of software.

When you start digging into Rhino, you find unexpected depth. JavaScript (unlike Perl, Python and Ruby, at least today) actually has a real specification, and Rhino follows it rigorously, aiming for complete SpiderMonkey compatibility within the bounds allowed by the different language platforms. Rhino also offers rich configurability, has well-defined multi-threading semantics, has a full set of hooks for debugging and profiling, and much more besides. There’s a lot under the hood.

Oh, and Sun is now bundling it with the JDK; it’s javax.script in Java 6. With that kind of endorsement it would have been hard to justify using anything else, even if some language other than Jython or JavaScript had been up for consideration.

With an endorsement like that and the potential for easy integration in an IDE with Google’s GWT you can expect to hear a lot more about Rhino on Rails in the coming year.

Default arguments in JavaScript

I sometimes run into people who need (whatever the reason may be) to be able to give default argument values in a JavaScript function. Similar to what you find in C++:

void foo(int a, int b = 42) {
//b is set to 42 automatically if no value is defined
...
}

I felt an urge to do it, so I sat down for a few minutes, trying to conjure something nifty that would be intuitive enough for even me to use.

The solution I came up with, and that hopefully will put an end to the default-arguments rant for good.

The solution is quite simple, and very intuitive. It may have some shortcomings but keep in mind I didn’t explicitly write it for you. You could probably modify my solution to fit your needs anyway.

I’ve seen a lot of developers (including myself, long time ago) using this pattern as a workaround for the lack of built-in support for default-arguments.

function foo(a, b)
{
a = typeof(a) != 'undefined' ? a : 42;
b = typeof(b) != 'undefined' ? b : 'default_b';
...
}

Which most developers probably find sufficient, I don’t.

The solution may look scary at a quick first glance, but bear with me. I’ll start by writing the framework-code. The code that will be required for this thing to work.

Function.prototype.defaults = function()
{
var _f = this;
var _a = Array(_f.length-arguments.length).concat(
Array.prototype.slice.apply(arguments));
return function()
{
return _f.apply(_f, Array.prototype.slice.apply(arguments).concat(
_a.slice(arguments.length, _a.length)));
}
}

See that wasn’t so scary :) .

In order for this to work you have to declare you functions in a special way. There are basically two ways of declaring a function.

function foo(a, b)
{
...
}

Is identical to (apart from the lexical difference, and some other minor things)

var foo = function(a, b)
{
...
}

In order for this solution to work you have to declare all functions on which you wish to have default-arguments the latter way.

Usage

var foo = function(a, b)
{
...
}.defaults(42, 'default_b');

Is identical to the first code-block but without adding any code in the function-body.

Example

var bar = function(a, b)
{
}.defaults('default_b');
bar();
// a = undefined, b = 'default_b'
bar(1);
// a = 1, b = 'default_b'
bar(1, 'some_value');
// a = 1, b = 'some_value'