What isthis
?
It depends on the situation. Let’s look at a few common scenarios.
The globalthis
Outside of a function,this
references the global object. In a browser environment, this is typically thewindow
object. In this scenario, you can set properties on the global object by referencing it with this:
this.name='Joe';console.log(window.name);// Joeconsole.log(name);// Joeconsole.log(this.name);// Joe
this
inside a function called on an object
Suppose we have the following object:
constjoe={name:'Joe',sayHello(){console.log(`Hello, I'm${this.name}`);}};
If we call thesayHello
function on thejoe
object like this:
joe.sayHello();// prints 'Hello, I'm Joe'
Thenthis
inside thesayHello
function refers to thejoe
object.
When you call a function on an object using dot notation like we have done above, you could say thatthis
refers to the object before the dot. This is sometimes called thereceiver.
If, however, we save a reference to thesayHello
function and call it via the reference, we receive a different result:
constgreet=joe.sayHello;greet();// prints "Hello, I'm undefined"
What happened? When there is no explicit receiver in a function call,this
refers to the global object. If nothing else has set aname
property on thewindow
object, this will printHello, I'm undefined
.
If some other code already set aname
property on thewindow
object, it will print that instead. Consider the following code:
name='Bob';constjoe={name:'Joe',sayHello(){console.log(`Hello, I'm${this.name}`);}};joe.sayHello();// prints "Hello, I'm Joe"constgreet=joe.sayHello;greet();// prints "Hello, I'm Bob"constben={name:'Ben',sayHello:joe.sayHello};ben.sayHello();// prints "Hello, I'm Ben"
this
inside an event listener
Another common scenario is an event listener. When an event listener is added, a callback function is specified to handle the event. When this callback is called,this
refers to the object that the event listener was added to.
document.querySelector('button.myButton').addEventListener('click',function(){this.style.background='red';});
Here we added aclick
listener to a button. When the button is clicked and the callback function is executed,this
refers to the button.
this
inside a callback
There are several useful functions onArray.prototype
such asforEach
,map
,reduce
, etc. Each of these takes a callback function as an argument.
Inside the callback passed to these functions,this
refers again to the global object.
constarr=[1,2,3];arr.forEach(function(item){console.log(this);});
When the above code is run in a browser, it will print thewindow
object to the console three times.
Consider the following code:
name='Bob';constjoe={name:'Joe',greet(people){people.forEach(function(person){console.log(`Hello${person}, I'm${this.name}`);});}};joe.greet(['Liz','Ben']);
The above code will produce the following output:
Hello Liz, I'm BobHello Ben, I'm Bob
Even though thegreet
function has athis
value of thejoe
object, inside the callback toforEach
the value ofthis.name
isBob
, which was set on thewindow
object.
How can we change this code so that thegreet
function printsJoe
instead ofBob
?
One way is to save a reference tothis
and reference that from inside the callback:
name='Bob';constjoe={name:'Joe',greet(people){constself=this;people.forEach(function(person){console.log(`Hello${person}, I'm${self.name}`);});}};joe.greet(['Liz','Ben']);
When we run this, it works as intended:
Hello Liz, I'm JoeHello Ben, I'm Joe
Why does this work? Because a function inherits the surrounding scope (thanks, closure), the value ofself
can be accessed from within the callback function.
This is generally frowned upon these days, as there are better ways to accomplish this, as discussed in the next section.
Changing the value ofthis
Using an arrow function
The easiest way to accomplish what the previous code sample does is to use an arrow function instead of thefunction() { ... }
syntax.
An arrow function does not get its ownthis
; rather, it inherits thethis
of its enclosing scope. We can rewrite the previous example using arrow functions:
name='Bob';constjoe={name:'Joe',greet(people){people.forEach(person=>console.log(`Hello${person}, I'm${this.name}`));}};joe.greet(['Liz','Ben']);
The output is the same as before:
Hello Liz, I'm JoeHello Ben, I'm Joe
The value ofthis
inside the arrow callback function is thejoe
object.
UseFunction.prototype.bind
There are several handy functions on the prototype ofFunction
. One of these isbind
. With this function you can change whatthis
refers to in a given function.
constjoe={name:'Joe',sayHello(){console.log(`Hello, I'm${this.name}`);}}constgreet=joe.sayHello;greet();
As we have already seen, the above code will not printHello, I'm Joe
because we are calling thesayHello
function without an explicit receiver. However, we can fix this by callingbind
:
constjoe={name:'Joe',sayHello(){console.log(`Hello, I'm${this.name}`);}}constgreet=joe.sayHello.bind(joe);greet();// prints "Hello, I'm Joe"
Here's whatbind
does: Callingbind
on a function like we did above returns anew function whosethis
value is bound to the first argument passed tobind
.
joe.sayHello
is a reference to thesayHello
function. We then callbind(joe)
on that function, which returns a new function wherethis
is bound to thejoe
object. So our code works as intended.
bind
can actually take more than one argument. That's beyond the scope of this post, but essentially it allows you to do partial application of functions.
UseFunction.prototype.call
orFunction.prototype.apply
Two other useful functions on theFunction
prototype arecall
andapply
. They both have the same end result, they just approach it slightly differently, as we will see in a moment.
constjoe={name:'Joe',greet(person){console.log(`Hello${person}, I'm${this.name}`);}}constgreet=joe.greet;greet('Ben');// prints "Hello Ben, I'm undefined"greet.call(joe,'Ben');// prints "Hello Ben, I'm Joe"greet.apply(joe,['Ben']);// prints "Hello Ben, I'm Joe"
As you can see,call
andapply
both accomplish what we want. But you might notice there's a slight difference in how they're used.
First, what do they have in common?call
andapply
both invoke a function with the first argument bound as thethis
value. So in the above example, when we callcall
andapply
on the function, thejoe
object is bound tothis
.
This is similar tobind
as shown above, but with one key difference.bind
returns anew function that will always have the specifiedthis
value for every invocation. In contrast,call
andapply
operate on the original function, and their effects apply only to that single invocation.
Now, back tocall
andapply
. What is the difference? The difference is how we specify the arguments to the function call.Function.prototype.call
takes a variable number of arguments. Each of these arguments are passed, in order, as arguments to the original function.
Function.prototype.apply
takes two arguments. The first, as we've seen, is thethis
value to use. The second argument is an array of the argument values to be passed to the function call. The difference is more apparent with a function call using multiple arguments. Consider the difference between these:
// These both call the greet function with joe as the this value, and three arguments: 'Ben', 'Liz', and 'Bob'greet.call(joe,'Ben','Liz','Bob');greet.apply(joe,['Ben','Liz','Bob]);
Other ways
There are yet other ways to affect the value ofthis
in a function call. One example isArray.prototype.forEach
. As we saw earlier,forEach
takes a callback function as its argument. However, it also takes an optional second argument. If specified, this argument will become the value ofthis
in the callback function:
constjoe={name:'Joe',greet(people){people.forEach(function(person){console.log(`Hello${person}, I'm${this.name}`);},this);}}joe.greet(['Liz','Ben']);
Notice in theforEach
call thatthis
was passed as the second argument after the callback. As long as this function is invoked like this:joe.greet(...)
, then the callback function will have the correctthis
value set.
Summary
The rules ofthis
in JavaScript can be a little tricky for beginners, but hopefully this post has helped clear up some confusion.
As we have seen, there are several ways to affect what thethis
value is during a function call. If you are using ES2015+, the easiest way is to just use an arrow function. If you can't use arrow functions, there are still several tools at your disposal such asbind
,call
, andapply
.
Top comments(1)
For further actions, you may consider blocking this person and/orreporting abuse