I am Hack Sparrow
Captain of the Internets.

JavaScript .bind() vs .apply() and .call()

So, what's the point of .bind() when we already have .apply() and .call()?

For those not familiar with .bind(), .apply(), and .call(), here is a quick summary with this object

var fruit = { name: 'Apple' }

and this function

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

on mind.

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.

showDetails.apply(fruit, ['small', 1])
-> Apple small: $1/kg

.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.

showDetails.call(fruit, 'medium', 5)
-> Apple medium: $5/kg

.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().

var bound = showDetails.bind(fruit, 'large', 10)
bound()
-> Apple large: $10/kg

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.

showDetails.bind(fruit, 'large', 10)()
-> Apple large: $10/kg

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

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

showDetails.bind(fruit, 'large', 10)()
-> Apple large: $10/kg

or in the returned bound function at a later time.

var bound = showDetails.bind(fruit)
bound('medium', 7)
-> Apple medium: $7/kg

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.

2 Responses to “JavaScript .bind() vs .apply() and .call()”

  1. Kevin Kim says:

    An excellent post. Do you have Twitter handle to follow?

  2. Captain says:

    It’s @hacksparrow.com

Make a Comment