Binding With Placeholders in Javascript

Function.prototype.bind is incredibly useful for binding initial arguments, but sometimes we want to bind arguments at other indices. This post offers one simple implementation of binding with C++ inspired placeholders for Javascript.

Goals

Motivating Example

Consider a function dot2 that calculates the two-dimensional dot product.

var dot2 = function(x1, y1, x2, y2) {
    return (x1 * x2) + (y1 * y2);
};

Function.prototype.bind can only bind initial arguments, such as binding x1 or x1, y1, and x2. To bind arguments like y1 or x1 and x2, we need to change dot2. This is often accomplished by manually forwarding arguments using an anonymous function.

var dotForwarded = function(y1, y2) {
    return dot2(10, y1, 10, y2);
};

dotForwarded(2, 3); // 106

Placeholders are a cleaner and clearer solution:

var dot10xs = placeholder(dot2, 10, _, 10, _);

dot10xs(2, 3); // dot2(10, 2, 10, 3) // 106

dot10xs(5, -1); // dot2(10, 5, 10, -1) // 95

placeholder(dot10xs, 5)(4, null, 2); // dot2(10, 5, 10, 4, null, 2) // 120

Implementation

placeholder takes a function f and a set of bound arguments. The bound arguments map argument indices to bound values, and may contains holes. For example, we can bind arguments at index 0 and 2, but leave index 1 open (along implicitly with all indices greater than 2). placeholder returns a function that fills in unbound arguments and forwards all arguments to f.

Bound arguments are provided as initial arguments to placeholder. The index of a bound argument in the call to placeholder determines the index of the argument in f being bound.

We choose _ to identify a hole. The selection of _ is arbitrary, any identifier can be used but that identifier must identify a unique object. This ensures that all types of arguments, including falsy values and undefined, are supported.

var _ = {};

One possible implementation of placeholder is given here. This implementation uses iteration to merge the bound arguments and callee arguments, and Function.prototype.apply to invoke f.

var placeholder = function(f /*, ...*/) {
    var bound = Array.prototype.slice.call(arguments, 1);
    return function(/*...*/) {
        var args = []; // Arguments to call f with.
        
        // Copy all bound elements into position
        for (var i = 0, len = bound.length; i < len; ++i)
            if (bound[i] !== _) // Skip holes
                 args[i] = bound[i];
        
        // Copy provided arguments into place
        var indx = 0;
        for (var i = 0, len = arguments.length; i < len; ++i) {
            // Skip already bound positions
            while (indx in args)
                 ++indx;
            
            // Add callee argument
            args[indx++] = arguments[i];
        }
        
        // Call the function with the bound arguments.
        //
        // You can easily adapt `placeholder` to bind `this`
        // like `Function.prototype.bind` but this implementation
        // ignores `this`.
        return f.apply(undefined, args);
    };
};

Performance

This JSPerf shows placeholder has high overhead compared to manually forwarding arguments. Every call to a function using placeholder requires two function calls and a lot of iteration, array manipulation, and allocation.

Therefore, placeholder is probably not a good solution for small math functions like dot2. But for more substantial interfaces, placeholder can make code cleaner and eliminate a lot manual argument forwarding.