Saturday, January 11, 2014

The JavaScript function F()

The JavaScript ECMA-262 standard 5th edition provides for Array-iterators like map() and reduce(). This means you don't need to write boiler-plate code for for -loops. Instead you can write things in a more functional way like this:
 [1,2,3].map(function (x){return x * 10});

What diminishes the utility of iterators like map() is that unlike some other languages, JavaScript does not have a shorthand syntax for anonymous functions.  You need to write a whole function-definition to call map(), as seen above.

So what could be done to reduce our workload and increase readability of our code even further? Answer:  Define function F() as follows:

  function F(aString)
  { var s, f2 ;
    s = 'f2 = function (x,y,z,w)'
      + '{return ' + aString + '}';
    eval(s);
    return f2;
  }

You can now make your code shorter like this:

  a =    [1,2,3].map(F('x * 10'));
  // vs. [1,2,3].map(function (x){return x * 10});
  ok (a[0] == 10);  
  ok (a[1] == 20);  
  ok (a[2] == 30);

  n =    [1,2,3,4].reduce(F('x + y'));
  // vs. [1,2,3,4].reduce(function (x,y){return x + y;});
  ok(n == 10);  


The function ok () used above is a simple way to express expected behavior of code in code. It can be defined simply as:

  function ok (arg)
  { if (!arg) {throw "ok() not ok"; }
  }

After publishing the first version of this post I got a suggestion from Georgiy Shibaev at Google+ JavaScript community to use the Function -constructor instead of eval.  And indeed it allows us to write the  function F() in a simpler way:

  function F (aString)
  { return new Function("x", "y", "z", "w", "return " + aString);
  }

Performance-wise there doesn't seem to be any difference between the two versions, tested on IE10. The Function -constructor still needs to do an eval internally, since you pass it a String argument.  I think this is a good use-case, and example how the Function-constructor is useful sometimes.

There are some caveats to using F ().  It can not create functions that would contain  if-then-else or other statements inside it. But you can use the ternary operator ? : .  If you need something more complicated then of course you can always write a full function definition.

Documentation of map() and reduce(),  including how to implement them on older browsers can be found at: https://developer.mozilla.org/

UPDATE:  My next blog-post Improved map() & reduce() improves this solution further.

UPDATE-2: In earlier version I called this function 'f()'.  But on 2nd thought this function is better named F (), to avoid possible conflicts with local variables, which typically at least start lowercase. I've made the change above


© 2014 Panu Viljamaa. This work is licensed under a Creative Commons Attribution 4.0 International License

No comments:

Post a Comment