Functional JavaScript Wizardry

Since EcmaScript 5, JavaScript has gotten nice collection functions like Array.map, Array.forEach, Array.filter and the usual suspects. However, they can be a bit clunky to use when dealing with objects. Look at the following piece of code:

[" fun", " ction ", "  al"].map(function(str) { return str.trim(); })
                           .join("");
//=> 'functional'

I map over an array of strings, trim each string, then join the resulting array of trimmed strings. Except it’s a bit too verbose for my tastes. I would like to write (and read) this instead:

[" fun", " ction ", "  al"].map(trim).join("");

The straightforward way to do that is to define the anonymous wrapper function that calls trim beforehand:

var trim = function(str) { return str.trim(); };
[" fun", " ction ", "  al"].map(trim).join("");
//=> 'functional'

Okay. Except that I don’t want to call just trim, I might call any function on Array, or Object, or any custom created object for that matter. And I certainly don’t want to write stupid wrapper functions each time.

The trim function already has a name: String.prototype.trim. But unfortunately, I cannot just write the following:

[" fun", " ction ", "  al"].map(String.prototype.trim).join("");
// TypeError: String.prototype.trim called on null or undefined

Because here map will call trim on each string in the array, but without any context object (a value for this). Instead, I would like map to pass each string as the context object to trim, in turn. We could redefine a custom version of map that works that way, but there’s a simpler solution.

Abstracting wrappers

What we really need is a function that transforms method calls into function calls. We need a function m2f to transform o.m(args) calls into f(o, args) calls, where f = m2f(o.m). Easy enough in JavaScript (bar the ugliness of dealing with the arguments special object):

function m2f(fun) {
  return function(/* args */) {
    var args = [].slice.apply(arguments);
    return fun.apply(args.shift(), args);
  };
}

var trim = m2f(String.prototype.trim);
[" fun", " ction ", "  al"].map(trim).join("");
//=> 'functional'

Good! Now I can use m2f on any “method”, and get back a more flexible function. For instance, I can use map on any “array-like” objects like strings, the special arguments object, NodeList and so on:

var map = m2f([].map); // Saving a few chars over `Array.prototype.map`
var up = m2f(String.prototype.toUpperCase);
map("abc", up)
// [ 'A', 'B', 'C' ]

map({0: 'albatros', 1: 'albatros', length: 2}, up);
// [ 'ALBATROS', 'ALBATROS' ]

function(){ return map(arguments, up) }(1, 2, 'abc', 'z')
// [ '1', '2', 'ABC', 'Z' ]

And of course, I can use m2f on any method, not just map. Here I populate an array object with functions from Array.prototype that work on any array-like object:

var array = {};
function isFunction(x) { return typeof x === 'function'; }
var names = Object.getOwnPropertyNames(Array.prototype);
names.forEach(function(name) {
  if (isFunction(Array.prototype[name]))
    array[name] = m2f(Array.prototype[name]);
})

array
// { join: [Function],
//   pop: [Function],
//   ... }

array.slice([0,1,2], -1)
//=> [ 2 ]

This replicates the Mozilla-specific “Array generics” extension. This extension is not available in V8-powered environment like nodejs.

This m2f function is quite handy when you are used to a functional style of programming. You can find it in Fogus’ Functional JavaScript under the name ‘invoker’. However, there is another way to define m2f using a powerful corner of EcmaScript 5.1, bind.

Calling and binding functions

Earlier, I said I could not just write:

var trim = String.prototype.trim;

Because JavaScript methods are really just functions. Now my trim is pretty useless on its own, because it expects a context object.

trim("  a ");
// TypeError: String.prototype.trim called on null or undefined

There’s a way to provide one to it, using call:

trim.call("  a ");
// 'a'

The call function takes a context, some arguments, and calls the receiver (a function) with them.

Another way to provide the context is to use the bind function. As its name implies, bind will return a function with the supplied context bound to it. Let’s see how it works:

var trimA = trim.bind("  a ");
trimA();
// 'a'

trimA.call("b");
// 'a'

Whenever trimA is called, it will call trim with "a" as context, even if trimA is supplied another context with call.

Binding call

Where does that lead us? Well, if we look at the precedent example again,

trim.call("  a ");

we see that trim.call is essentially the function we want. But we’d rather use trim directly, without calling call on it. How to achieve that?

Notice that call is itself a method call. So we can extract it on its own, and use it to call trim. But since it’s an extracted method, we still need to use call on it,

var call = trim.call;

call(trim, "  a ");
// TypeError: object is not a function

call.call(trim, "  a ");
// 'a'

Since we are calling call on itself, the notation is a bit overloaded, but it works. Note that now, trim is just an argument; we can put any other method in there,

call.call([].map, "abc", up);
// [ 'A', 'B', 'C' ]

In fact, there’s nothing tying call to trim. We could have defined call without trim, by taking it on Function.prototype directly,

var call = Function.prototype.call;

Nevertheless, the greatest benefit of this transformation is that we can now use bind to fix the first argument of call to a specific function.

var map = call.bind([].map);

map("abc", up);
// [ 'A', 'B', 'C' ]

And we just redefined m2f!

var m2f = call.bind;
var map = m2f([].map);
// TypeError: Bind must be called on a function

Except it doesn’t work … because we did not provide a context to bind. Putting it all together,

var call = Function.prototype.call;
var bind = call.bind;
var m2f = bind.bind(call);

Alternatively, since bind is not tied to call, it might be clearer to just write:

var call = Function.prototype.call;
var bind = Function.prototype.bind;
var m2f = bind.bind(call);

Which finally gives this one-liner gem:

var m2f = Function.prototype.bind.bind(Function.prototype.call);

A very useful tool for writing terse JavaScript.

A variant of this form was given by Dave Herman and explained by Erik Kronberg, which I read thrice without really understanding what was going on. This article is my attempt to derive this magic one-liner in a way that makes sense for me.