bind Function

By  on  

We oftentimes assume that "native" APIs within the browser are fast -- at least faster than shims we create or have been using.  I was surprised to recently read this StackOverflow thread which asserts that Function.prototype.bind is much slower than what you can shim.  The thread cites JS Perf links (which unfortunately don't work at the moment) but assuming the statements about speed are correct, let's check out how we can create our own bind function.

JavaScript bind Function

Creating a basic bind function is incredibly easy, as the aforementioned provided:

function bind (fn, ctx) {
    return function bound () {
        return fn.apply(ctx, arguments);
    };
}

// Usage:
bind(this.someFunction, this);

This is the simplest possible bind function but it doesn't accommodate for additional arguments you can provide to bind, which is why a more complete function is more complicated:

function bind (fn, ctx/* , arg1, arg2 */) {
  return (function (prependedArgs) {
    return function bound () {
      // Concat the bound function arguments with those passed to original bind
      var args = prependedArgs.concat(Array.prototype.slice.call(arguments, 0));
      return fn.apply(ctx, args);
    };
  })(Array.prototype.slice.call(arguments, 2));

// Usage
// anotherArg is first arg to onClick, then the event
bind(this.onClick, this, anotherArg);

This more complete method merges the passed-in arguments with the arguments provided to the individual function call (an Event, for example, if bind was used on an click event).

I cannot tell you with any certainty that Function.prototype.bind is in fact super slow, and if so, it's across every browser.  It is interesting, however, to explore these native API speeds in an effort to make our apps as fast as possible.

Know more about bind speed?  Please share!

Recent Features

Incredible Demos

Discussion

  1. “is much slower than what you can shim” if speaking about V8, that was true until relatively recent updates: https://v8project.blogspot.co.uk/2016/06/release-52.html

    Now .bind is on par with any regular function.

    • True that v8 has improved it, but it still has a long ways to go in other browsers AFAIK. :(

    • As far as I remember, FF didn’t have this problem for a while?

  2. I think I would pick a different name than bind(..) for anything that’s not the same as the behavior in the built-in Function.prototype.bind(..), just to reduce confusion, especially if you might have both in use in the same code base.

    I also dislike the fact that the built in Function.prototype.bind(..) conflates both this binding and argument partial-application. Those should have been in simpler separate functions, like bind(..) and partial(..).

    So maybe we should recommend like this:

    function bindThis(fn,thisObj) {
       return function bound() {
          return fn.apply(thisObj,arguments);
       };
    }
    
    // using ES6 goodies
    function partial(fn,...args1) {
       return function partial(...args2) {
          // pass along this context
          return fn.call(this,...args1,...args2);
       };
    }
    

    Now, we can use those behaviors separately, or compose them in various ways:

    function foo(x,y,z) {
       console.log( x + y + z + this.w );
    }
    
    var o = { w: 4 };
    
    var f = bindThis( partial(foo,1), o );
    var g = partial( f, 2 );
    
    g( 3 );  // 10
    
  3. I honestly don’t see any value in replicating, if not for polyfill purpose, native APIs.

    There’s so much new stuff to learn these days, I wouldn’t waste time on something that 99.9% of the time is not your performance issue in your app.

    Moreover, using workarounds won’t help anyone: more user-land/error-prone code that needs to be downloaded and less real-world data that can show bottlenecks so that JS engines can optimise slow paths.

    Everybody lose, and no better quality whatsoever will be delivered to final users.

    My 2 cents

    • Unless you wrote a 3D engine, you don’t need this kind of optimization. Worst, the gain you have now could be a loss in future: browsers JS engines are often updated and this affect performance (better or worst).
      Also be carefull with performance measurements which don’t take account impact on memory: what append when your application eat lot of memory and you use this trick which use closures?

  4. Dylan

    I find https://kpdecker.github.io/six-speed/ interesting to see which ES6 features are not yet particularly fast.

  5. Andrzej

    These implementations are still not exactly equivalent for native function – bind function has less priority in terms of this binding then new operator and these implementations don’t take it into account. It doesn’t make much difference but maybe this is why it takes a bit longer as well?

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!