bind Function
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!
“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?
I think I would pick a different name than
bind(..)
for anything that’s not the same as the behavior in the built-inFunction.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 boththis
binding and argument partial-application. Those should have been in simpler separate functions, likebind(..)
andpartial(..)
.So maybe we should recommend like this:
Now, we can use those behaviors separately, or compose them in various ways:
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
+1
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?
I find https://kpdecker.github.io/six-speed/ interesting to see which ES6 features are not yet particularly fast.
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?