This is the final installment of my three-parter on JavaScript function methods. The first part covered call()
, the second part covered apply()
, and this time it’s all about bind()
. All three are great tools to have, but as you’ll see, bind()
works a little differently than the others.
Creating Functions with bind()
While call()
and apply()
invoke a function immediately, bind()
does not. Instead, it copies an existing function, but with a new this
and argument list bound to it. It’s up to you to invoke this new function when you want.
In this example, a new function is created with 'meow'
and 'woof'
essentially hard-coded as arguments (ignore this
for now, we’ll discuss it next).
function sayThings(something, somethingElse) {
console.log(something + ' and ' + somethingElse);
}
boundSayThings = sayThings.bind(this, 'meow', 'woof');
boundSayThings(); // logs "meow and woof"
Controlling Scope
Just like with call()
and apply()
, the first argument passed to bind()
allows you to control scope by specifying what this
refers to when inside the function.
function sayFavoriteFood() {
console.log(this.favoriteFood);
}
// create a desmond object
var desmond = {
favoriteFood: 'tuna'
};
// create a maya object
var maya = {
favoriteFood: 'ribeye'
};
// create a function bound to the desmond object
var desmondSayFavoriteFood = sayFavoriteFood.bind(desmond);
// create a function bound to the maya object
var mayaSayFavoriteFood = sayFavoriteFood.bind(maya);
desmondSayFavoriteFood(); // logs "tuna"
mayaSayFavoriteFood(); // logs "ribeye"
Callback Functions
bind()
is useful for creating callback functions that need arguments passed in. These argument values are “locked-in”, even if something changes them before the function has a chance to execute. To demonstrate, let’s start with an example that doesn’t use bind()
.
function logValue(value) {
console.log('i = ' + value);
}
// logs "i = 3", "i = 3", "i = 3" with 1 second delays between them
// (probably not what you wanted)
for (var i = 0; i < 3; i++) {
setTimeout(function() {
logValue(i);
}, i * 1000);
}
logValue()
is called 3 times, but thanks to setTimeout()
, the calls don’t start happening until after the for
loop has completed. By that point, the value of i
has incremented to 3 — so that’s what gets logged. It doesn’t matter what i
was when each timeout was created, only what i
is when each timeout fires.
Let’s try again, but with bind()
.
function logValue(value) {
console.log('i = ' + value);
}
// logs "i = 0", "i = 1", "i = 2" with 1 second delays between them
// (probably what you wanted)
for (var i = 0; i < 3; i++) {
setTimeout(logValue.bind(this, i), i * 1000);
}
This time we used bind()
to bind logValue()
with the value of i
at the moment each timeout was created. So even though i
becomes 3 before any timeouts have fired, each call to logValue()
is still holding on to its own bound value of i
, giving us the intended result.
Binding Partial Argument Lists
You can still pass additional arguments into a function created by bind()
, even if you’ve already bound some arguments to it. The additional arguments are added to the end of the argument list. This lets you set up a function that has some arguments essentially hard-coded, then reuse that function with only the remaining arguments being variable.
function greet(me, you) {
console.log('Hi, I\'m ' + me + '. Nice to meet you, ' + you + '!');
}
var willGreet = greet.bind(this, 'Will');
willGreet('Joni'); // logs "Hi, I'm Will. Nice to meet you, Joni!"
willGreet('Mike'); // logs "Hi, I'm Will. Nice to meet you, Mike!"
Browser Support
bind()
is newer than call()
or apply()
, but no worries. It’s supported in just about any browser you’d care about… unless you care about IE8 or below. In which case, consider using Underscore’s bind()
(documentation here) or jQuery’s proxy()
(documentation here) to do the job.
Conclusion
bind()
is an interesting one. The concept of calling a function on a function to create a new function might sound bizarre at first, but it’s not so bad, and the utility it provides is definitely worth giving it a chance.
That wraps up my three-parter on JavaScript function methods. I hope you found it worthwhile. Thanks for reading!