Hi everyone, and welcome to another exciting edition of Boring JavaScript! Today, we cover a way to change the ‘this’ associated with a function – called the bind() method.
Don’t like to read? Skip to seeing the video!
Tying Things Together
In a nutshell, the bind() method let’s you assign a new ‘this’ to a function, so that if the function accesses ‘this’ (normally to insure it is in the right scope), it will access the ‘this’ assigned to it by ‘bind’.
Confused? Let’s look at an example:
const animals = [
{ type: "Cat", name: "Fluffy" },
{ type: "Dog", name: "Fido" },
{ type: "Horse", name: "Mr. Ed" },
{ type: "Cow", name: "Betsy" },
{ type: "Coyote", name: "Wile E." },
{ type: "Road Runner", name: "Beep Beep" },
{ type: "Dolphin", name: "Flipper" },
{ type: "Whale", name: "Moby Dick" },
{ type: "Lizard", name: "Larry" }
];
function getAnimalName (animalType) {
const animal = this.find( animal => animal.type === animalType);
return animal.name;
}
const myAnimal = "Cat";
const myAnimalName = getAnimalName(myAnimal);
console.log(`\nThe name of my ${myAnimal} is ${myAnimalName}.`);
If you were to run the above in Node, you will get an error saying the ‘this.find is not a function’. Why is that? It’s because we’ve defined our function ‘getAnimalName’ in the global namespace, and thus ‘this’ points to the global namespace. There is no ‘find’ method there, so the code errors out.
And that is where ‘bind()’ comes in. By changing Line 19 above to this:
const myAnimalName = getAnimalName.bind(animals)(myAnimal);
… we tell JavaScript to make the ‘animals’ array our ‘this’. Now when you execute the code, you get a report on your animal. That is because ‘this’ is now the ‘animals’ array, and of course, there is a ‘find()’ method on JavaScript arrays.
Back to Real Life
Yeah, I know, the above example was strange. How about a real life example?
Let’s say we have a zoo that has a collection of animals. We can get the names of the animals, and we can get a name of a random animal sent to us once every second for 10 seconds (why? Because we’re weird – and to simulate accessing a server). To get that once-per-second name, we use ‘setInterval()’ to call that function which randomizes a name for us.
So we create a class called ‘Zoo’, populate it with animals, create a function to get a name of a animal (a copy of the code earlier), a method to use the “setInterval()” to start our random routine (we’ll call it ‘showRandomAnimals’), and finally a routine called by “setInterval()” to get the random name (called ‘getRandomAnimal’).
Easy as pie.
class Zoo {
animals = [
{ type: "Cat", name: "Fluffy" },
{ type: "Dog", name: "Fido" },
{ type: "Horse", name: "Mr. Ed" },
{ type: "Cow", name: "Betsy" },
{ type: "Coyote", name: "Wile E." },
{ type: "Road Runner", name: "Beep Beep" },
{ type: "Dolphin", name: "Flipper" },
{ type: "Whale", name: "Moby Dick" },
{ type: "Lizard", name: "Larry" }
];
timer;
count = 0;
getAnimalName(animalType) {
const animal = this.animals.find( animal => animal.type === animalType);
return animal.name;
}
showRandomAnimals() {
this.count = 10;
this.timer = setInterval(this.getRandomAnimal, 1000)
}
getRandomAnimal () {
const animalNumber = Math.floor(Math.random() * this.animals.length);
const animal = this.animals[animalNumber];
console.log(`The name of the Zoo ${animal.type} is ${animal.name}.`);
this.count--;
if (this.count === 0) {
clearInterval(this.timer);
}
}
}
const myZoo = new Zoo();
console.log(`\nThe name of my Personal Cat is ${myZoo.getAnimalName('Cat')}.\n`);
myZoo.showRandomAnimals();
Except when you run the code, it blows up.
Why? Because code like ‘setInterval’ – including ‘setTimeout’ and all the events generated by JavaScript – are setup in another namespace rather than your class or application (depending upon if you are in Node or a Browser). Because of that, ‘this’ is no longer associated with your class as you would have expected, and so Line 27 crashes.
The fix is easy – use bind() to reassign the ‘this’ to the “setInterval()”. Line 23 changes to this:
this.timer = setInterval(this.getRandomAnimal.bind(this), 1000)
And now your 10 random animals over 10 seconds works like a champ.
This is where “bind()” can really shine. If you have a class where you are calling routines that might be out of scope (such as “getRandomAnimal” above), then “bind()” gives you a quick way of insuring your methods are called with correct scope.
I Shot an Arrow Into the Air
What about arrow functions? Can we reassign “this” there, too?
Let’s find out. Going back to our first example, make the function into an arrow function.
const animals = [
{ type: "Cat", name: "Fluffy" },
{ type: "Dog", name: "Fido" },
{ type: "Horse", name: "Mr. Ed" },
{ type: "Cow", name: "Betsy" },
{ type: "Coyote", name: "Wile E." },
{ type: "Road Runner", name: "Beep Beep" },
{ type: "Dolphin", name: "Flipper" },
{ type: "Whale", name: "Moby Dick" },
{ type: "Lizard", name: "Larry" }
];
const getAnimalName = (animalType) => {
console.log(this);
const animal = this.find( animal => animal.type === animalType);
return animal.name;
}
const myAnimal = "Cat";
const myAnimalName = getAnimalName(myAnimal);
console.log(`\nThe name of my ${myAnimal} is ${myAnimalName}.`);
If you were to run this as-is, you’d get the “this.find is not a function” error on Line 15, and the console.log on Line 14 will print out an empty object. These are expected because “this” has no binding inside of an arrow function.
So let’s add in the “bind()” on Line 20:
const myAnimalName = getAnimalName.bind(animals)(myAnimal);
Run it. What did you get?
The same thing. Arrow functions still will ignore any bindings of “this” to them.
Bottom line: Don’t use “bind()” on arrow functions.
The Video
Here is the video we created for this blog post.
Shameless Plug
Check us out all over the web!
Check out all our videos at: https://www.boringjavascript.com
Check out everything at: https://www.thevirtuoid.com
Facebook: https://www.facebook.com/TheVirtuoid
Twitter: https://twitter.com/TheVirtuoid
YouTube: https://www.youtube.com/channel/UCKZ7CV6fI7xlh7zIE9TWqgw
Categories: Boring JavaScript Javascript
thevirtuoid
Web Tinkerer. No, not like Tinkerbell.
Creator of the game Virtuoid. Boring JavaScript. Visit us at thevirtuoid.com
Leave a Reply