次の方法で共有


[JavaScript] Is Function.prototype.bind() a bad practice?

There are many articles out there explaining what Function.prototype.bind() does for us, but it seems few of them bothered explaining what happened behind the scene for the bind() function. Chances are - if you work on JavaScript - you probably have been using bind() all around in your code. When bind() is called on a function, it returns a new function with same implementation and "this" points to the object of your choice, but is that it?

bind() is massively used in many JavaScript projects, which is not surprising, given the magic it offers. A recent scan through the source code I work on indicated more than 2300 of them. The large scale of usage raise my concerns about what impact this massive usage might bring - especially on performance and memory footprint. Clearly, I was not alone: Patrick Mueller wrote this great article specifically about the matter: bind() considered harmful.

Something we should always keep in mind is that bind() returns a new function, as Patrick mentioned, which is basically separated from the original one (except that they share the implementation). Instead of the garbage it might create (frankly I think it's unlikely that someone would write code to bind() functions in a loop), I'd mention that you should be careful when distinguishing both copies and which one you should use, especially when it comes to add/remove event listeners. Given the easy-going nature of JavaScript, you will only realize you used the wrong one at run time.

An interesting fact about bind() is that it could do a bit more than just binding an object to "this", it also accepts a list of arguments which you could pass to the function you call bind() on. The interesting part is how few this feature has been used - out of 2300+ uses of bind() in our code, less than 50 of them were accompanied with optional arguments. And you could easily see why - just by looking at the example on MDN:

 function list() {
 return Array.prototype.slice.call(arguments);
 }
 
 var list1 = list(1, 2, 3); // [1, 2, 3]
 
 // Create a function with a preset leading argument
 var leadingThirtysevenList = list.bind(undefined, 37); // this could easily creates problem if bound function is not properly named
 
 var list2 = leadingThirtysevenList(); // [37]
 var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]

If you failed to maintain a good naming convention on the bound functions, it could soon become error-prone. The problem is that the clients of this bound function might not have knowledge on what arguments it already travels with. In fact, many developers just assign the bound function back to the original function itself, since they really don't needs an object that only holds the implementation. Just as Patrick mentioned in his article - this could easily gets in the way of debugging. 

Is var that = this a better solution?

Obviously, we can't get rid of bind(), regardless it's actual harmful or not. JavaScript utilize callback (or eventhandler, whichever way you call it) far more than a lot of other languages. And due to the dynamic binding of JavaScript, you probably can't live without it. But there is a simple (and elegant, in my opinion) alternative though - you could use the pattern of defining a local variable that saves off the mighty "this" object in the scope. And naturally it will be available to any callbacks you defined via closure. "that" seems to be a popular choice of name for this matter, I've seen other names such as "context" or "scope" used in this matter as well.

In theory this should be better than using bind(), assuming your sole purpose is to provide access to this in the callback (as mentioned above, this takes up most part of bind()'s usage). Since it performs a simple variable assignment, and potentially bypassed some logic that bind() might have (eg. analyzing arguments). I should mention this is just an educated guess from me, but doing a simple test on jsPerf seems to support it: bind-vs-that.

Your thoughts? Feel free to let me know by leaving a comment!

Comments

  • Anonymous
    November 18, 2014
    Very informative as I didn't know that bind returns a new function.  I recently did some load testing on my website and noticed a not-insignificant amount of the allocation was going towards code blocks.