84

A previous poster asked Function.bind vs Closure in Javascript : how to choose?

and received this answer in part, which seems to indicate bind should be faster than a closure:

Scope traversal means, when you are reaching to grab a value (variable,object) that exists in a different scope, therefore additional overhead is added (code becomes slower to execute).

Using bind, you 're calling a function with an existing scope, so that scope traversal does not take place.

Two jsperfs suggest that bind is actually much, much slower than a closure.

This was posted as a comment to the above

And, I decided to write my own jsperf

So why is bind so much slower (70+% on chromium)?

Since it is not faster and closures can serve the same purpose, should bind be avoided?

Community
  • 1
  • 1
Paul
  • 26,170
  • 12
  • 85
  • 119
  • 12
    "Should bind be avoided" --- unless you're doing it thousands time one a page - you shouldn't care of it. – zerkms Jul 14 '13 at 09:58
  • 1
    Assembly of an asynchronous complex task from small pieces might require something that looks exactly like that, in nodejs, because the callbacks need to be aligned somehow. – Paul Jul 14 '13 at 09:59
  • I'd guess it's because browsers haven't put as much effort into optimising it. See Mozilla's code (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility) for implementing it manually. There's every chance browsers are just doing that internally, which is much more work than a quick closure. – Dave Jul 14 '13 at 10:03
  • 1
    Indirect function calls (`apply/call/bind`) are in general much slower than direct ones. – georg Jul 14 '13 at 10:24
  • @zerkms And who's to say one isn't doing it thousands of times? Due to the functionality it provides I think you might be surprised at how common this might be. – Andrew May 30 '20 at 01:20

2 Answers2

150

Chrome 59 update: As I predicted in the answer below bind is no longer slower with the new optimizing compiler. Here's the code with details: https://codereview.chromium.org/2916063002/

Most of the time it does not matter.

Unless you're creating an application where .bind is the bottleneck I wouldn't bother. Readability is much more important than sheer performance in most cases. I think that using native .bind usually provides for more readable and maintainable code - which is a big plus.

However yes, when it matters - .bind is slower

Yes, .bind is considerably slower than a closure - at least in Chrome, at least in the current way it's implemented in v8. I've personally had to switch in Node.JS for performance issues some times (more generally, closures are kind of slow in performance intensive situations).

Why? Because the .bind algorithm is a lot more complicated than wrapping a function with another function and using .call or .apply. (Fun fact, it also returns a function with toString set to [native function]).

There are two ways to look at this, from the specification point of view, and from the implementation point of view. Let's observe both.

First, let's look at the bind algorithm defined in the specification:

  1. Let Target be the this value.
  2. If IsCallable(Target) is false, throw a TypeError exception.
  3. Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order.

...

(21. Call the [[DefineOwnProperty]] internal method of F with arguments "arguments", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false}, and false.

(22. Return F.

Seems pretty complicated, a lot more than just a wrap.

Second , let's see how it's implemented in Chrome.

Let's check FunctionBind in the v8 (chrome JavaScript engine) source code:

function FunctionBind(this_arg) { // Length is 1.
  if (!IS_SPEC_FUNCTION(this)) {
    throw new $TypeError('Bind must be called on a function');
  }
  var boundFunction = function () {
    // Poison .arguments and .caller, but is otherwise not detectable.
    "use strict";
    // This function must not use any object literals (Object, Array, RegExp),
    // since the literals-array is being used to store the bound data.
    if (%_IsConstructCall()) {
      return %NewObjectFromBound(boundFunction);
    }
    var bindings = %BoundFunctionGetBindings(boundFunction);

    var argc = %_ArgumentsLength();
    if (argc == 0) {
      return %Apply(bindings[0], bindings[1], bindings, 2, bindings.length - 2);
    }
    if (bindings.length === 2) {
      return %Apply(bindings[0], bindings[1], arguments, 0, argc);
    }
    var bound_argc = bindings.length - 2;
    var argv = new InternalArray(bound_argc + argc);
    for (var i = 0; i < bound_argc; i++) {
      argv[i] = bindings[i + 2];
    }
    for (var j = 0; j < argc; j++) {
      argv[i++] = %_Arguments(j);
    }
    return %Apply(bindings[0], bindings[1], argv, 0, bound_argc + argc);
  };

  %FunctionRemovePrototype(boundFunction);
  var new_length = 0;
  if (%_ClassOf(this) == "Function") {
    // Function or FunctionProxy.
    var old_length = this.length;
    // FunctionProxies might provide a non-UInt32 value. If so, ignore it.
    if ((typeof old_length === "number") &&
        ((old_length >>> 0) === old_length)) {
      var argc = %_ArgumentsLength();
      if (argc > 0) argc--;  // Don't count the thisArg as parameter.
      new_length = old_length - argc;
      if (new_length < 0) new_length = 0;
    }
  }
  // This runtime function finds any remaining arguments on the stack,
  // so we don't pass the arguments object.
  var result = %FunctionBindArguments(boundFunction, this,
                                      this_arg, new_length);

  // We already have caller and arguments properties on functions,
  // which are non-configurable. It therefore makes no sence to
  // try to redefine these as defined by the spec. The spec says
  // that bind should make these throw a TypeError if get or set
  // is called and make them non-enumerable and non-configurable.
  // To be consistent with our normal functions we leave this as it is.
  // TODO(lrn): Do set these to be thrower.
  return result;

We can see a bunch of expensive things here in the implementation. Namely %_IsConstructCall(). This is of course needed to abide to the specification - but it also makes it slower than a simple wrap in many cases.


On another note, calling .bind is also slightly different, the spec notes "Function objects created using Function.prototype.bind do not have a prototype property or the [[Code]], [[FormalParameters]], and [[Scope]] internal properties"

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • If f = g.bind(stuff); should f() be slower than g(stuff) ? I can find this out pretty quick, I'm just curious if the same thing happens every time we call a function no matter what instantiated that function, or if it depends where that function came from. – Paul Jul 14 '13 at 10:36
  • 4
    @Paul Take my answer with some skepticism. All this might be optimized in a future version of Chrome (/V8). I've seldom found myself avoiding `.bind` in the browser, readable and understandable code is much more important in most cases. As for speed of bound functions - [Yes, bound functions will stay slower at the moment](http://jsperf.com/bind-vs-native-bind-run) , especially when the `this` value is not used in the partial. You can see this from the benchmark, from the specification and/or from the implementation independently [(benchmark)](http://jsperf.com/bind-vs-native-bind-run). – Benjamin Gruenbaum Jul 14 '13 at 10:42
  • I wonder if: 1) anything has changed since 2013 (it's been two years now) 2) since arrow functions have *this* lexically bound - are arrow functions slower by design. – Kuba Wyrostek Jun 28 '15 at 12:52
  • 1
    @KubaWyrostek 1) No, 2) No, since bind isn't slower by design, it's just not implemented as fast. Arrow functions haven't landed in V8 yet (they landed and then were reverted) when they will we'll see. – Benjamin Gruenbaum Jun 28 '15 at 12:57
  • 1
    Would future calls to a function that has already had "bind" applied be any slower? I.e. a: function(){}.bind(this)... are future calls to a() any slower than if I never binded in the first place? – wayofthefuture Aug 25 '15 at 10:33
  • Hi, bind is no longer slow in Chrome 59+ and is only slightly slower since Chrome 51. – Benjamin Gruenbaum May 08 '17 at 22:12
  • When you say "bind is slower", do you mean actual `bind` calls themselves or calls to the "bound" function? – Armen Michaeli Sep 25 '21 at 11:39
  • @amn this is no longer the case - but used to be both. – Benjamin Gruenbaum Jan 30 '22 at 11:29
6

I just want to give a little bit of perspective here:

Note that while bind()ing is slow, calling the functions once bound is not!

My test code in Firefox 76.0 on Linux:

//Set it up.
q = function(r, s) {

};
r = {};
s = {};
a = [];
for (let n = 0; n < 1000000; ++n) {
  //Tried all 3 of these.
  //a.push(q);
  //a.push(q.bind(r));
  a.push(q.bind(r, s));
}

//Performance-testing.
s = performance.now();
for (let x of a) {
  x();
}
e = performance.now();
document.body.innerHTML = (e - s);

So while it is true that .bind()ing can be some ~2X slower than not binding (I tested that too), the above code takes the same amount of time for all 3 cases (binding 0, 1, or 2 variables).


Personally, I don't care if the .bind()ing is slow in my current use case, I care about the performance of the code being called once those variables are already bound to the functions.

Andrew
  • 5,839
  • 1
  • 51
  • 72
  • If you're going to completely overhaul the entirety of my answer/code, just write your own answer. Edits are for smaller or cosmetic changes. If you disagree, don't care, screw off. – Andrew May 09 '23 at 05:17