Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Joe Attardi
Joe Attardi

Posted on

     

All about `this` in JavaScript

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
Enter fullscreen modeExit fullscreen mode

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}`);}};
Enter fullscreen modeExit fullscreen mode

If we call thesayHello function on thejoe object like this:

joe.sayHello();// prints 'Hello, I'm Joe'
Enter fullscreen modeExit fullscreen mode

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"
Enter fullscreen modeExit fullscreen mode

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"
Enter fullscreen modeExit fullscreen mode

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';});
Enter fullscreen modeExit fullscreen mode

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);});
Enter fullscreen modeExit fullscreen mode

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']);
Enter fullscreen modeExit fullscreen mode

The above code will produce the following output:

Hello Liz, I'm BobHello Ben, I'm Bob
Enter fullscreen modeExit fullscreen mode

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']);
Enter fullscreen modeExit fullscreen mode

When we run this, it works as intended:

Hello Liz, I'm JoeHello Ben, I'm Joe
Enter fullscreen modeExit fullscreen mode

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']);
Enter fullscreen modeExit fullscreen mode

The output is the same as before:

Hello Liz, I'm JoeHello Ben, I'm Joe
Enter fullscreen modeExit fullscreen mode

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();
Enter fullscreen modeExit fullscreen mode

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"
Enter fullscreen modeExit fullscreen mode

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"
Enter fullscreen modeExit fullscreen mode

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]);
Enter fullscreen modeExit fullscreen mode

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']);
Enter fullscreen modeExit fullscreen mode

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)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
sanderdebr profile image
sanderdebr
Passionate front-end developer.
  • Joined

Thanks for this explanation, very informative! Was struggling with the this keyword in a big class earlier today.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Software engineer, author, blogger, lifelong learner.
  • Location
    Boston
  • Education
    BS Computer Science, UMass Lowell
  • Work
    Principal Software Engineer at Black Duck Software
  • Joined

More fromJoe Attardi

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp