Dynamic precompiling of JavaScript functions for fun and profit

Tags:

Hey it’s Friday so it’s time to try a fun JavaScript hack – Let’s go and precompile some functions!

What does “precompiling” mean? In principle, it means we take a function which takes multiple parameters and pre-define some of them. All this is done dynamically, so your code can do it on the fly.

Let’s check out how this works and what benefits it has.

The basics

I got this idea from currying – the act of taking a function and “partially calling” it. Essentially, when currying a function, you pass it only some of the parameters it needs and you get back a function that takes the remaining parameters.

For example (non-functional code, dont’ try it)

function example(a, b, c) {
  /* do something */
};
 
//Use an imaginary curry-function to curry example
var curriedExample = curry(example, 10, 20);
 
//Call the curried function with 30. 
curriedExample(30);
 
//The above is equivalent to calling:
example(10, 20, 30);

So by currying you can make it simpler to call a function which takes same parameters but some change.

However, currying itself reduces speed. This is due to the trickery you need to actually produce the curried function. I thought it might be fun to see if you could actually speed it up – passing less parameters should in theory increase speed afterall…

Precompiling

As you may know, you can get the source code of a JavaScript function with func.toString(). You might also know that you can create new functions from strings by using new Function('args', 'function body');.

What if you were to actually replace the parameters with their values in the function body?

That’s what we shall do – Instead of precompiling, perhaps this should be called recompiling.

Simple example implementation

I wrote a very quick and simple example implementation of this kind of a compiler function.

function precompile(functionName) {
	var fun = String(fnc);
	//Capture function args and body into capture groups
	var argsBody = fun.match(
		/^function[^(]*\(([^)]*)\)\s*{([\s\S]*)}$/
	);
 
	//Get argument names
	var argNames = argsBody[1].split(/\s*,\s*/g);
 
	//Function body
	var body = argsBody[2];
 
	//Replace arguments with provided values
	var argumentValues = Array.prototype.slice.call(arguments, 1);
	for(var i = 0; i < argumentValues.length; i++) {
		body = body.replace(new RegExp(argNames[i], 'g'), argumentValues[i]);
	}
 
	var remainingArguments = argNames.slice(i);
	return new Function(remainingArguments.join(','), body);
}

By using the above function, we can create precompiled versions where the parameter names are replaced with their values. It basically just goes and replaces the variable name literals with their respective values.

var compiledExample = precompile(example, 10, 20);
compiledExample(30);

The good parts

  • The precompiled function is actually faster than the original, but in my tests only in Opera and Firefox.
  • In Chrome there was not much of a difference.

The bad parts

  • The speed difference is tiny.
  • It’s really, really, really slow in IE

In closing

So I guess we can say that if you want to get every last bit of performance and you’re targetting non-IE browsers, this could be another tool to add to your disposal.

However, the example implementation I showed here only works with numbers. It would be simple to change it so it would also support strings and booleans, but it will become tricky when you want it to support objects.

In the end, I’m not sure if I would recommend this approach… It was fun trying to do this though ;)

More fun JavaScript reading:
TankWar Online: a JavaScript based cannons game
I wrote a Sim City clone in JavaScript