What the "this"?

5 simple rules to tame JavaScript's most confusing keyword

JavaScript's this keyword is confusing at the best of times. But what if I told you that 99% of the time, five simple rules are all you need to use this like a pro?

this may well be the most confusing part of JavaScript you’ll run up against — it sure is for me. And what’s worse, unlike other confusing things (like the arguments fake-array), there’s no modern substitute for it! You need this for classes, and thus for many modern JavaScript libraries. You’re kinda stuck with it.

It’s no wonder then that I recently received an e-mail with a question about this. In particular, the reader wanted some help understanding the below block of code from my article When to use arrow functions with React:

Within this render function, the call to handleClick is preceded by this.. The this on line 4 refers to the component instance, so shouldn’t the this on line 7 point to the component instance too?

class BrokenButton extends React.Component {
  render() {
    return (
      <button onClick={this.handleClick} style={this.state}>        Set background to red
      </button>
    )
  }

  handleClick() {
    this.setState({ backgroundColor: 'red' })  }
}

Firstly, thanks for the great question! This is something that most JavaScript developers have struggled with at some point — myself included. To really understand what’s going, there’s no substitute for trying things out, so later in this article I’m going to walk you through a bunch of live demos (after introducing the 5 rules that’ll help you understand this). But I’m getting ahead of myself! Let me start by answering the question:

If a call to handleClick is preceded by this., then the value of this within the function will behave as you expect. The issue is, the above example never actually calls handleClick().

To put it another way: calling this.handleClick() will change the function’s behavior, setting the value of the this keyword within the handleClick function. In contrast, passing this.handleClick just passes the function as a value — without changing the behavior at all.

Confused? You’re not alone. But keep reading — we’ll get to the bottom of this together.

#It’s all in the call

When working with JavaScript functions, the value of this depends on the way in which the function was called — as opposed to the way in which the function was defined.

This can result in some truly boggling behavior. For example, try uncommenting the final two lines in the below example. Like actually try it out. Seeing is believing. I’ll wait here.

index.js
const robot = { 
  dream: "kill all humans",
  sleepTalk() {
    console.log(`Hey baby, wanna ${this.dream}?`)
  }
}

robot.sleepTalk()

// // You'd think this would do the same thing as the line above.
// const robotSleepTalk = robot.sleepTalk
// robotSleepTalk()

    Did you give it a shot? Great! So you’ll have noticed that while calling robot.sleepTalk() prints a classic line from Futurama… calling exactly the same function a couple lines later causes the app to die.

    So what’s going on? Let’s see if you can’t figure it out yourself, using the 5 rules of this.

    #The 5 rules of this

    The value of this in a JavaScript function can vary based on how the function was called. There are far too many rules to list them all right here, but 99% of situations are covered by just three rules:

    1. When a function is called as a method of an object, e.g. obj.someFunction(), the value of this within that function will be the object on which it was called.
    2. When a function is called using its call() or apply() methods, the value of this inside the function is provided as the first argument to call() or apply().
    3. When a function is called by itself (i.e. without a leading .), the value of this will either be undefined in strict mode, or the global object (e.g. window) outside of strict mode. But you’re probably in strict mode, so it’s probably going to be undefined.

    There are two important exceptions to these rules:

    1. You can use a function’s bind method to return a version of a function where this will always equal the first argument to bind.
    2. The value of this within an arrow function will always equal the value of this at the time the function was created.

    These rules and exceptions are a bit of a mouthful, so let’s take a look at some live examples.

    #Methods and this

    The simplest and most obvious way for a function to get a value for this occurs when the function is called as a method. In this case, this is set to the object on which it was called.

    index.js
    class Counter {
      constructor(initialCount = 0) {
        this.count = initialCount
      }
    
      increment() {
        return this.count += 1
      }
    }
    
    const counter = new Counter()
    
    console.log('initial count', counter.count)
    console.log('after increment', counter.increment())
    console.log('and again', counter.increment())

      Keep in mind that it doesn’t matter how a function is defined — only how it is called. This means that there are many different ways to create a counter with the same API as above:

      index.js
      function increment() {
        return this.count += 1
      }
      
      const counter1 = {
        count: 0,
        increment,
      }
      
      const counter2 = {
        count: 0
      }
      counter2.increment = counter1.increment
      
      console.log('counter1', counter1.increment(), counter1.increment())
      console.log('counter2', counter2.increment(), counter2.increment())

        #this on plain functions

        When you call a function by itself in a strict-mode application, this will be undefined.

        index.js
        function increment() {
          return this.count += 1
        }
        
        console.log(increment())

          Obviously, it’s unlikely you’re ever going to write code like the above. But this rule can still cause you all sorts of grief, especially when this rule is applied to functions that were designed to be called as methods.

          For example, say you pass a counter instance’s increment method to a setTimeout() function.

          index.js
          class Counter {
            constructor(initialCount = 0) {
              this.count = initialCount
            }
          
            increment() {
              this.count += 1
              console.log('value after increment', this.count)
            }
          }
          
          const counter = new Counter
          
          counter.increment()
          setTimeout(counter.increment, 100)

            See how the call to increment by setTimeout() doesn’t behave as you’d expect? This is because internally, setTimeout() doesn’t have access to the counter object — it only has access to the increment function. As a result, increment is called by itself, and this is undefined for the duration of that call.

            Beware

            Be careful when using this in event handlers — the code calling the event handler probably won’t set this to what you want it to be!

            #this on arrow functions

            When using arrow functions, you’ll find that the value of this is fixed to whatever it was when the function was defined. When the function is created at the top level, this will usually be undefined. But where things get interesting are when you define arrow functions within other functions. In this case, the value of this within the arrow function will reflect the value of this within the function that created it.

            One place where arrow functions come in handy is class constructors. Any arrow functions defined in a constructor will always have a value of this that points to the class instance, no matter where they’re called from — making them perfect for event handlers.

            index.js
            class Counter {
              constructor(initialCount = 0) {
                this.count = initialCount
                this.increment = () => this._increment()
              }
            
              _increment() {
                this.count += 1
                console.log('value after increment', this.count)
              }
            }
            
            const counter = new Counter
            
            counter.increment()
            setTimeout(counter.increment, 100)

              Arrow functions are also great for event handlers in React’s class components, as they reduce the mental overhead for accessing instance methods like setState().

              #Specifying this using bind, call and apply

              If you need to force a specific value of this but can’t use an arrow function, then bind, call and apply are your friends. These functions allow you to specify a function’s arguments and this value programmatically.

              I won’t go into too much detail on how these work — if you’re interested, you can follow the above links to read the details on MDN. For good measure though, here’s a demo using all three:

              index.js
              function increment() {
                return this.count += 1
              }
              
              const counter = { count: 0 }
              
              increment.bind.call(increment, counter).apply()
              
              console.log('did it increment?', !!counter.count)

                As bind, call and apply are themselves functions… well, you can use them on each other. But please, don’t do this at home (or especially at work).

                #The this quiz

                Now that you’ve seen this in action, let’s take another look at the broken dreaming bending robot:

                const bendingRobot = { 
                  dream: "kill all humans",
                  sleepTalk() {
                    console.log(`Hey baby, wanna ${this.dream}?`)
                  }
                }
                
                bendingRobot.sleepTalk()
                const bendingRobotSleepTalk = bendingRobot.sleepTalk
                bendingRobotSleepTalk()

                Now do you know why the last line throws an error, while calling bendingRobot.sleepTalk() works as expected? Have a think about it, and then check your answer below.

                this doesn’t care about where sleepTalk was defined — it only cares about how it was called. In the case where it’s called using robot.sleepTalk(), the value of this will be robot. However, when called as a bare function, this will be undefined.

                #Exercise: fixing the React component

                Let’s finish up by taking another look at the broken React component from the start of this article. Now that you’ve learned about the different ways to control the value of this, can you fix the component so that clicking the button changes the background color?

                App.js
                index.js
                import React from 'react'
                
                export class App extends React.Component {
                  render() {
                    return (
                      <button onClick={this.handleClick} style={this.state}>
                        Set background to red
                      </button>
                    )
                  }
                
                  handleClick() {
                    this.setState({ backgroundColor: 'red' })
                  }
                }
                Build In Progress

                How’d you go — were you able to make the button work?

                Keep in mind that there’s no right ways to do this — I’ve provided one solution on the solution tab, but it’s by no means the only one. If you come up with another solution, I’d love to see it — you can let me know with a tweet!

                #The best approach to this?

                So now that you know all about this, I want to finish up by discussing the best approach to using it:

                In my opinion, the best use of this is no use of this.

                At best, this is confusing. At worst, it can be the cause of obscure and unpredictable bugs. If you have the choice, the easiest thing to do is to just avoid it. And luckily, if you’re using React, hooks make it easier than it’s ever been to avoid this completely!

                With this in mind, in a future article, I want to dive into the workings of a custom hook from one of my production codebases. And I’m looking for your help to decide which one! Here are the options:

                • useLatestSnapshot() - a hook for integrating with Firebase, simplifying access to the latest Snapshot of a Firestore Document or Query.
                • useMediaQuery() - a hook that returns a boolean reflecting whether the browser currently matches the given media query.
                • usePopupTrigger() - a hook that manages the state of popup menu, independent of its presentation.

                If you’d like to have a say in which of these I write about, it’s easy — just send a quick email or tweet telling me which of the above options you’d like to see. Of course, I’d love to hear any of your other JavaScript questions too! I won’t promise to respond to every message, but I’m always looking for ideas for content that can help give people a leg up. I can’t wait to hear from you — and until next time, happy coding!

                About James

                Hi! I've been playing with JavaScript for over half my life, and am building Frontend Armory to help share what I've learned along the way!

                Tokyo, Japan