If you’ve been writing software for a while, you’ve experienced the feeling that comes from ambient awareness of a function. You see it in use, but never completely understanding how it works, and you certainly don’t dare use it yourself. And then a special day comes in which you suddenly figure out how to use it, everything clicks, and you promise yourself you’ll be using it every place you can possibly justify doing so. Yesterday this happened to me with the bind() function.

As a abstract example, let’s say we have a program where we want to do x but before we can do that, we need to do something that will take an indefinite, nonlinear amount of time. In the simplest of terms, that could look like this:

var asyncCall = function() {
  var x = setTimeout(function() {
    console.log('timeout ended');
  }, parseInt(Math.random() * 1000) );
}

After a indefinite number of milliseconds, timeout ended will log to the console when we call asyncCall(). What if x had to be used elsewhere in our program, not just in asyncCall()? We need a separation of these two pieces of logic so they’re not in a single block of code. We can achieve this by passing in a function to asyncCall that gets called back once asyncCall has completed. Let’s look at what that could look like:

var x = function() {
  console.log('timeout ended');
};

var asyncCall = function(callback) {
  setTimeout(function() {
    if ( callback && typeof callback === 'function' ) {
      callback();
    }
  }, parseInt(Math.random() * 1000) );
}

Now we you call asyncCall(x); we’ll get the exact same result as the original program, but now we have better abstraction. But what if we wanted to abstract our x function so that it took a string parameter and logged this to the console, instead of a string literal? Let’s give our x function a better name and see what that might look like:

var log = function(str) {
  console.log(str);
};

Now if we do asyncCall(log) we have a problem: undefined gets logged to the console because the str parameter is not set! We need a way to pass a str argument to the log() callback function. If we didn’t know any better, we might be tempted to try this:

asyncCall(log('timeout ended'));

This won’t actually generate any JavaScript errors, interestingly enough. All it does is instantly execute the log() function, complete with its str parameter, and then it begins executing asyncCall() with no callback argument passed. If we weren’t checking to make sure that our callback existed and was a function, we would get this error after the asyncCall() timeout completed:

Uncaught ReferenceError: log is not defined

This is where the bind() function comes in! Check this out:

asyncCall(log.bind(null, 'timeout ended'));

The bind() function does exactly what it sounds like—it takes a comma separated list of arguments and binds them in order to the function that called it. The first parameter to bind() sets what the this object will be inside of the function.1 In other words, you could do something like this:

var s = {
    t: 44,
    func: function() {
        console.log(this.t);
    }
};
s.func.bind({t:34})(); // logs 34
// or, more simply, using the very similar apply() function
s.func.apply({t:34}); // logs 34

The beautiful thing about bind() is that it doesn’t require us to change anything in log() or asyncCall(). We can now pass the former to the latter with as many arguments as we need, and be confident that it will execute at the appropriate time exactly as we intended it to. That’s power.


  1. From Mozilla’s developer docs:

    The bind() function creates a new function (a bound function) with the same function body (internal call property in ECMAScript 5 terms) as the function it is being called on (the bound function’s target function) with the this value bound to the first argument of bind(), which cannot be overridden.

    Got to love how terse their definitions are.

    ↩︎