JavaScript: bind() vs apply() and call()
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.
.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.
.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()
.
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.
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
or in the returned bound function at a later time.
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
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.