« Previous Tutorial Next Tutorial »
Last week we talked about how function.call
and function.apply
work, and used them to do some interesting stuff, including chaining constructors. Today we're going to take a quick look at a few more use cases. Reminder that call
and apply
are basically the same except call
takes a this
argument followed by as many other arguments as you want to give it, where apply
takes a this
argument and then a single array of other arguments. We're just going to work with call
for examples, because I find the syntax a little less obtuse.
First, let's define some data that we'll be using:
const youtubeMusicians = [ { name: 'Rob Scallon', instrument: 'guitar', vocalist: false }, { name: 'Davie504', instrument: 'bass', vocalist: false }, { name: 'Andrew Huang', instrument: 'everything', vocalist: true }, { name: 'Mary Spender', instrument: 'guitar', vocalist: true }, { name: 'Adam Nealy', instrument: 'bass', vocalist: false }, ];
I could go on … YouTube is a fantastic place for following musicians. But that seems like plenty.
One of the simplest ways in which to use call
and apply
is to specify a specific this
value for an existing function. This is very similar to what we were doing with constructor stringing in the last tutorial, but not quite as complex. We're basically just replacing an argument like, say, data
with the function's this
property instead. Observe!
function introduceMusician() { console.log(`Introducing ${this.name}, who plays ${this.instrument} and is ${this.vocalist ? '' : 'not '}a vocalist.`); } introduceMusician(youtubeMusicians[1]); // broken! introduceMusician.call(youtubeMusicians[3]); // works!
Note that this only works with traditional function declarations and not arrow functions, because the latter don't have a this
value to which you can assign things.
Generally speaking, I … don't see a ton of use for this approach if you're just using plain objects like we are, but in certain cases it can be handy, for example if you're passing a large class that already has a lot of things attached to its own this
property.
Another thing we can do with call
and apply
is use them to invoke an inline anonymous function, even one that has its own values attached to the this
property. This one's weird, but we'll talk through it. Here's the code:
for (let i = 0; i < youtubeMusicians.length; i += 1) { (function() { this.nameTheSingers = function() { if(this.vocalist) { console.log(`${this.name} sure can sing!`)} } this.nameTheSingers(); }).call(youtubeMusicians[i]); }
This is a bit of a convoluted example, but we're running a loop and, within that loop, defining an anonymous constructor function, giving it a nameTheSingers
method, and then immediately using that method. However, none of this would run without the .call
because in each loop we'd just define the function, but never actually execute it, so this.nameTheSingers
will never get run without .call
(also we'd have no value for this.vocalist
and this.name
). This is, again, kind of a weird use case but in a scenario in which you need a temporary, anonymous function with a lot of methods, it can be very handy.
That's all I've got for this week! Keeping the "quick" in Quick Hits this time around. Next week, we're going to take a look at maps, which are like objects … but different.
See you then!
As always, you can download example files for each of these tutorials from the JS Quick Hits github repo.
Enjoying these quick hits? You can get them five days early by subscribing to our weekly newsletter.