What is "this"?
In a nutshell, this
is a keyword in JavaScript that holds a reference
to the object that you're currently working with. It's a lot easier to understand with a visual example:
class Human {
constructor(gender, age) {
this.gender = gender;
this.age = age;
}
}
const man = new Human("male", 23);
const woman = new Human("female", 26);
console.log(man.gender); // male
console.log(woman.gender); // female
...here, a Human
class with two properties is initialized. We then create two instances
of that class: man
and woman
. In the context of
man
, this
refers to that object, so it becomes
man.gender
and man.age
. In the context of
woman
, this
refers to
woman.gender
and woman.age
.
this
simply refers to the object that's currently using the constructor. This
is all done implicitly, but what if we want to explicity control what this
refers to?
call(), apply(), and bind()
By default, the this
keyword is implicitly defined by its context, giving you no
control over what it refers to, although it can be controlled by explicitly defining it with one of three methods —
call()
, apply()
, or
bind()
. Let's start with the call()
method:
const movie = {
title: "The Dark Knight",
releaseDate: 2008,
};
function showMovieDetails() {
console.log(`${this.title} was released in ${this.releaseDate}.`);
}
showMovieDetails();
// undefined was released in undefined.
...when showMovieDetails
is invoked, the console logs the title
and releaseDate
as undefined
, which makes sense because
the showMovieDetails
function has nothing to do with the movie
object, so this
holds no context. Luckily, we can use the call()
method to assign the the properties of movie
to this
...
showMovieDetails.call(movie);
// The Dark Knight was released in 2008.
...the call()
method takes in the name of the object that you want to assign the
this
keyword to. Pretty simple, right? The apply()
method is super similar...
showMovieDetails.apply(movie);
// The Dark Knight was released in 2008.
...what's the difference? Well, let's add a bit more to our example...
const movie = {
title: "The Dark Knight",
releaseDate: 2008,
};
function showMovieDetails(firstName, lastName) {
console.log(
`${this.title} was released in ${this.releaseDate}, and was directed by ${firstName} ${lastName}.`
);
}
showMovieDetails.call(movie, "Christopher", "Nolan");
// The Dark Knight was released in 2008, and was directed by Christopher Nolan.
showMovieDetails.apply(movie, "Christopher", "Nolan");
// UncaughtTypeError: CreateListFromArrayLike called on non-object
...the showMovieDetails
function has two parameters now:
firstName
and lastName
.
Both call()
and apply()
can receive additional arguments, but how come the apply()
method throws an
error? Well, the only difference between both methods is that apply()
takes
in arguments as an array, whereas call()
receives them individually...
showMovieDetails.call(movie, "Christopher", "Nolan");
// The Dark Knight was released in 2008, and was directed by Christopher Nolan.
showMovieDetails.apply(movie, ["Christopher", "Nolan"]);
// The Dark Knight was released in 2008, and was directed by Christopher Nolan.
...whereas call()
and apply()
immediately invoke the specified
function with the given value, the bind()
method works a little differently. Instead,
it returns a new function that when invoked, uses the given value...
const batmanFacts = showMovieDetails.bind(movie, "Christopher", "Nolan");
batmanFacts();
// The Dark Knight was released in 2008, and was directed by Christopher Nolan.
...this gives us the liberty to invoke the batmanFacts()
function anywhere in our program
while preserving the context of this
, which in this case is the movie
object.
To summarize, call()
, apply()
, and
bind()
are all used to control what the this
keyword refers to. call()
takes in arguments individually, whereas
apply()
takes them in as an array. Both of these methods immediately invoke the function,
unlike bind()
, which returns a new function that can be executed later.