What is the difference between bind(), apply(), and call()?#

Let's start with a quick summary of .bind(), .apply(), and .call(), with this object:

var fruit = { name: 'Apple' };

and this function:

function showDetails(size, price) {
  console.log(this.name + ' ' + size + ': $' + price + '/kg');
}

All three of them are function methods (functions attached to a function object), which execute the function with a custom this context object.

.apply() will execute the function showDetails with the fruit object as the context for this within it. The function parameters will be passed in an array after the context object as shown below.

Run
Clear

.call() will execute the function showDetails with the fruit object as the context for this within it. The function parameters will be passed in a comma-separated format after the context object.

Run
Clear

.bind() will return a function, which can execute the function showDetails with the fruit object as the context for this within it. The function parameters are passed like .call().

Run
Clear

As you can see, .apply() and .call() are executed immediately, while .bind() returns a function, which can be executed at a later time. And, if you want to make .bind() execute immediately too, you can do so this way.

Run
Clear

This brings us to the question, "Why do we have .bind(), when we could just use .call() or .apply()?".

What makes .bind() unique and powerful#

While .bind() might seem similar to .apply() and .call() in the ability to specify a context object, it is more flexible and powerful as elaborated in the points below.

1. .bind() provides two opportunities (at different times) for passing the function parameters

The function parameters for a bound function can be specified in the .bind() function

Run
Clear

or in the returned bound function at a later time.

Run
Clear

You might encounter situations, where you have the context but the parameters will become available only at a later time. In such situations, .bind() will come to your rescue.

2. .bind() preserves the context of this for future execution

Have you ever needed to do var that = this or var self = this? If you use .bind(), you won't need to do that anymore.

Before:

var cat = {
  name: 'Neo',
  showName: function () {
    var self = this
    // this could be any context-switching function - async, callback
    setTimeout(function () {
      console.log(self.name);
      // this.name will be undefined
    });
  }
}

cat.showName()
// Neo

After:

var cat = {
  name: 'Neo',
  showName: function () {
    var printer = function () {
      console.log(this.name);
    }.bind(this); // bind the current `this` (reference to `cat`) to the `printer` function, while we have a valid reference
    setTimeout(printer);
  }
}

cat.showName()
// Neo

The context of this can also be preserved by the use of ES6 arrow functions.

3. .bind() can preserve the current parameters for future execution

Have you ever created an immediately executing anonymous function to pass on the parameters to a function, which would have otherwise lost the context / been overwritten? With .bind(), you have a much cleaner way of handling such situations.

For example, by the time the following code wants to do console.log(pets[i]), the value of i is already 3, and therefor will print undefined three times, instead of the expected names of the pets.

var pets = ['Neo', 'Bud', 'Kalia']

for (var i = 0; i < pets.length; i++) {
  // simulated async operation
  setTimeout(function () {
    console.log(pets[i]);
  })
}

The problem can be solved by creating a closure on each iteration and passing the current value of i to it, so that each setTimeout have their own isolated array index value, not affected by the final incremented i.

for (var i = 0; i < pets.length; i++) {

  (function (i) {
    setTimeout(function () {
      console.log(pets[i]);
    })
  })(i);

}

Although the solution works, it doesn't look neat and can become a source of memory leaks.

We can re-write the functionality using .bind() without the need for creating additional closure, as shown below.

for (var i = 0; i < pets.length; i++) {

  var printer = function (i) {
    console.log(pets[i]);
  }.bind(undefined, i);

  setTimeout(printer);

}

And, there you have three good reasons why we have .bind() apart from .apply() and .call().

Any time you are working with async/nested code and feel like you have to hack around to maintain context, it is very likely that .bind() is what you are looking for.

References#

  1. MDN - bind
  2. MDN - apply
  3. MDN - call