In my second day at Catalyst, we did some programming koans, and I learned something really interesting-- JavaScript, every function has a toString() and the Function constructor can create functions from strings.

Exploring the relationship between functions and strings

Let's say we've already defined a function called "product" in the browser console:

var product = function(a, b) {
  //gratuitous comment
  return a * b;
}

product(3, 4); //returns 12

We can see the string representation of our function by calling the toString() method:

product.toString(); // returns the following:

"function (a, b) {
  //gratuitous comment
  return a * b;
}"

Build functions from strings with new Function

This is where things start to get fun. First we'll put the entire function body in a string, using \n\ to escape our new line. Then we pass the argument names and function body into the new Function constructor, each wrapped in strings. This creates a new function, named f, that returns the product of its arguments, just as product() does.

var s = "  //gratuitous comment\n\
return a * b";

var f = new Function("a", "b", s);

f(3, 4); //returns 12

f.toString(); //returns the following:
"function anonymous(a, b) {
  //gratuitous comment
  return a * b
}"

Continuing down the rabbit-hole

Strings can be sliced, modified, messed with to create other functions. By doing a substitution on the string, we can make a function that adds instead of multiplying.

var string2 = s.replace('*', '+');
var f = new Function("a", "b", string2);
f(3,4); //return 7

The next step would be to create a function that takes a string as input and returns an array of strings-- one string for each parameter and one for the function body. The two variable version of this is as follows:

var crazy = function(str) {
  //Note: JS regex don't work properly on multiline matches
  //This code can be simplified for future browsers that do
  var unblank = function (x) {
    return (x !== ") && (x !== "}");
  }
  fbody = str.match(/.*/gm).filter(unblank);
  fbody[0] = fbody[0].match(/\((.*)\)/)[1];
  args = fbody.shift().split(', ');
  fbody = fbody.join('\n');
  
  return new Function(args[0], args[1], fbody);
}

The crazy function above parses the toString() of a function into arguments and a function body, wraps them in strings, passes them into a new anonymous function constructor and returns it. It is the inverse function of toString().

var g = crazy(f.toString());

g(3,4) === f(3,4); // true
g(6,7) === f(6,7); // true

If JavaScript included some higher order functional tools like apply(), this crazy function could be extended to handle any arbitrary number of function arguments. Fortunately, these tools can be added to JavaScript without too much work.

If anybody has any experience using this feature for anything practical, I'd love to hear about it!

Leave a Reply

Your email address will not be published. Required fields are marked *

4 replies