Why is Function.prototype.bind slow? Why is Function.prototype.bind slow? google-chrome google-chrome

Why is Function.prototype.bind slow?


Based on http://jsperf.com/bind-vs-emulate/6, which adds the es5-shim version for comparison, it looks like the culprit is the extra branch and instanceof that the bound version has to perform to test if it's being called as a constructor.

Each time the bound version is run, the code that gets executed is essentially:

if (this instanceof bound) {    // Never reached, but the `instanceof` check and branch presumably has a cost} else {    return target.apply(     that,     args.concat(slice.call(arguments))    );    // args is [] in your case.    // So the cost is:    // * Converting (empty) Arguments object to (empty) array.    // * Concating two empty arrays.}

In the V8 source code, this check appears (inside boundFunction) as

if (%_IsConstructCall()) {    return %NewObjectFromBound(boundFunction);}

(Plaintext link to v8natives.js for when Google Code Search dies.)

It is a bit puzzling that, for Chrome 16 at least, the es5-shim version is still faster than the native version. And that other browsers have rather varying results for es5-shim vs. native. Speculation: maybe %_IsConstructCall() is even slower than this instanceof bound, perhaps due to crossing native/JS code boundaries. And perhaps other browsers have a much faster way of checking for a [[Construct]] call.


It's impossible to implement a fully-featured bind in ES5 alone. In particular sections 15.3.4.5.1 through 15.3.4.5.3 of the spec cannot be emulated.

15.3.4.5.1, in particular, seems like a possible performance burden: in short bound functions have different [[Call]] internal properties, so calling them is likely to take an unusual and possibly more complicated code path.

Various other specific un-emulatable features of a bound function (such as arguments/caller poisoning, and possibly the custom length independent of original signature) could possibly add overhead to each call, although I admit it's a bit unlikely. Although it looks like V8 doesn't even implement the poisoning at the moment.

EDIT this answer is speculation, but my other answer has something more approaching evidence. I still think this is valid speculation, but it's a separate answer, so I'll leave it as such and just refer you to the other one.


The V8 source code for bind is implemented in JS.

The OP doesn't emulate bind because it doesn't curry arguments the way bind does. Here is a fully featured bind:

var emulatebind = function (f, context) {  var curriedArgs = Array.prototype.slice.call(arguments, 2);  return function () {    var allArgs = curriedArgs.slice(0);    for (var i = 0, n = arguments.length; i < n; ++i) {      allArgs.push(arguments[i]);    }    return f.apply(context, allArgs);  };};

Obviously, a quick optimization would be to do

return f.apply(context, arguments);

instead if curriedArgs.length == 0, because otherwise you have two unnecessary array creations, and an unnecessary copy, but perhaps the native version is really implemented in JS and does not do that optimization.

Caveat: This fully featured bind does not correctly handle some corner cases around this argument coercion in strict mode. That might be another source of overhead.