The Call & Apply Methods
In this lecture, we're going to go back to the this
keyword and learn how we can set the this
keyword manually and also why we would want to do that.
Let's say we are an airline, and in this case, Emirates. Hence, let's create a very simple object for this airline with a very simple booking method.
const emirates = {
airline: 'Emirates',
iata: 'EK',
bookings: [],
book(flightNumber, passengerName) {
console.log(
`${passengerName} booked flight ${this.iata}${flightNumber} on ${this.airline}`
);
this.bookings.push({
flight: `${this.iata}${flightNumber}`,
passengerName,
});
},
};
Let's now try to book some flights.
emirates.book(123, 'John Doe');
emirates.book(456, 'Jane Doe');
From the above output, we can see that everything works just as expected. What's important to understand here is that the this keyword points to the emirates object because that's the object on which the book method was called.
But now, let's say that after some years, the Emirates Group created a new airline called Emirates Executive.
const emiratesExecutive = {
airline: 'Emirates Executive',
iata: 'EE',
bookings: [],
};
Emirates Executive, an entirely new airline, must be able to book flights just like the Emirates airline. How could we do that? Copying the same book method from the emirates
object into the emiratesExecutive
object is a bad practice.
Instead, we will take the method and store it in an external function. Making it possible for us to reuse that function for all of the different airlines. That is, we create a new book function and set tit to emirates.book
.
/**
* This is possible because JavaScript has First Class Functions!
*/
const book = emirates.book;
Now if we try to call this new book function, what do you think will happen?
book(123, 'Mike Smith');
We get an error. And I guess you already know why. But anyway, we get this error because our new book
function is now a regular function call. And as we learned in one of the previous lectures, the this
keyword points to undefined
, at least in strict mode.
Now the question is, how do we fix this problem? Or, in other words, how de we tell JavaScript that we want to create a booking on the new Emirates Executive airline or Emirates airline? To fix this problem, since our new book
function is a copy of emirates.book
, we need to tell JavaScript explicitly what the this
keyword should point to. That is, if we want to book an Emirates flight, the this
keyword should point to emirates
, but if we want to book an Emirates Executive flight, then the this
keyword should point to emiratesExecutive
.
To tell JavaScript what the this
keyword should point to, there exist three methods: call
, apply
, and bind
. When we first talked about the this
keyword, I mentioned these three methods back then, and so now we are going to use them at least the call
and apply
methods.
Before showing you how to use the call
method, always remember that a function is just an object, and objects have methods; therefore, functions can have methods, too, and the call
method is one of them.
To use the call
method, the first argument is the object on which the method should be called, followed by the list arguments that should be passed to the method.
book.call(emiratesExecutive, 789, 'Mike Smith');
console.log(emiratesExecutive);
From the above output, you can see that we have our bookings array, which contains an object with flight number EE789
and passenger name 'Mike Smith'
, which belongs to emiratesExecutive
.
Let's recap what happened here. This time, we did actually not call the book
function ourselves. Instead, we called the call
method and it's then this call
method, which will call the book
function with the this
keyword set to emiratesExecutive
or any other object we pass as first argument. Hence this allow us to manually and explicitly set the this
keyword of any function that we want to call. Then all the arguments after the first one are simply the arguments of the original function.
Let's do the same thing with Emirates airline.
book.call(emirates, 509, 'Jake Turner');
console.log(emirates);
The string that we get from the output is correct, and in the Emirates bookings array we now have three bookings. That, of course, happened because this time, we set the this
keyword inside of the function call to emirates
.
Now that we have a way of manually manipulating the this keyword using the call method, we can now create more airlines into the Emirates Group like the Emirates SkyCargo.
const emiratesSkyCargo = {
airline: 'Emirates SkyCargo',
iata: 'ES',
bookings: [],
};
book.call(emiratesSkyCargo, 963, 'Kevin Hill');
The second method as mentionned above is the apply method. The apply method does exactly the same thing. The only difference is that the apply method does not receive a list of arguments after the this keyword. Instead, it's going to take an array of arguments.
const flightBookingData = [738, 'Billy Bob'];
book.apply(emiratesSkyCargo, flightBookingData);
The apply method is no longer used that much in modern JavaScript because now, we have a better way of doing the same thing.
// This is equivalent to
book.apply(emiratesSkyCargo, flightBookingData);
//This
book.call(emiratesSkyCargo, ...flightBookingData);
To finish, with modern JavaScript, I always use the call
method and then spread the arguments like above. In the next lecture we are going to learn about the bind
method. It's more important than the call
and apply
methods, but does the same thing.