When you start learning JavaScript, at some point you come across the this keyword and might find it confusing what it refers to at different points in your code. In this article, I aim to give you a robust framework to know exactly what it is, and what it refers to at any point in your code.

What is the 'this' keyword?

We call it a keyword because it's a special term in the JavaScript language. This means you cannot call a variable this or a function this as it is reserved, much like the keywords function and var.

But it's a special variable that exists all the time, only the value changes based on the piece of JavaScript being called. A more straightforward example is in a class definition:

class VirtualPet {
  constructor (name) {
    this.name = name
  }

  introduceYourself() {
    console.log(`Hello, I'm ${this.name}`)
  }
}
An example of this used in a JavaScript class

In the above example we use the this keyword twice. The first inside the constructor to set the name when initializing our virtual pet, and the second in the introduceYourself class method which can use this to refer to the single instance of the VirtualPet. This means we can have two virtual pets and each can refer to their own name.

const cat = new VirtualPet(`Fluffy`)
const dog = new VirtualPet(`Spike`)

cat.introduceYourself()
// Hello, I'm Fluffy

dog.introduceYourself()
// Hello, I'm Spike
How to call class methods that invoke the this keyword

But outside of classes, the this keyword has different meanings when we are dealing with regular functions and arrow functions.

function regularFunction() {
  console.log(`I'm a regular function`)
}

const arrowFunction = () => {
  console.log(`I'm an arrow function`)
}
Regular function vs arrow function in JavaScript

'this' and regular functions

By default, regular functions change the this variable when they are called. Here's how you remember it.

A picture of a royal palace in the mountains
A telegram from the royal palace arrives at your at your home. As you read it, you discover you are formally invited to a prestigious function at the royal palace where you'll be among the likes of royalty and dignitaries. The evening will consist of a silver service of seven courses followed by formal dancing in the ballroom.

During your attendance, you notice that your surrounding are vastly different to what you are accustomed to. You see masterpiece paintings and intricate architecture. You stop and think. Now this is something else.

So, remember the following. When you use a regular function, it's like physically going to a function where your surroundings, which we'll call this is now different.

Example of the global this in the browser window using the Dev Tools

This is what we call our global variable.

Now let's say we're in a our VirtualPet and we want to show the change in this by calling a regular function.

// Create a global variable called isCurrentlyAt in the `Window` object
var isCurrentlyAt = 'the royal function'
function attendFunction(name) {
  // this is now the global `Window` object
  console.log(`${name} is at ${this.isCurrentlyAt}`)
}

class FairytaleCharacter {
  constructor(name) {
    this.name = name
    this.isCurrentlyAt = 'home'
  }

  whereAreYou() {
    console.log(`${this.name} is at ${this.isCurrentlyAt}`)
  }

  acceptRoyalInvitiation() {
		attendFunction(this.name)
  }
}

const cinderella = new FairytaleCharacter('Cinderella')

cinderella.whereAreYou()
// Cinderella is at home
cinderella.acceptRoyalInvitiation()
// Cinderella is at the royal function
Calling a regular function and seeing how the this references changes to the global object

So what just happened here? When inside a class method, the this reference is the instance of the class. This is so you can do things like this.name or this.whereAreYou() to call other methods within the class.

Where the regular function reveals it's behavior is when it's called from within the class, and as illustrated, when the function is called, it has a this reference to the global object — which is the Window object in a browser.

For the purpose of this illustration, I inserted a global variable called isCurrentlyAt to show how this.isCurrentlyAt changes depending on whether it's inside a class method (whereAreYou) or a regular function (attendFunction).

Now, if you were to declare that function as an inline function inside the class method, the this variable ends up being undefined. So it loses the global reference when nested inside a class method.

// Create a global variable called isCurrentlyAt in the `Window` object
var isCurrentlyAt = 'the royal function'

class FairytaleCharacter {
  constructor(name) {
    this.name = name
    this.isCurrentlyAt = 'home'
  }

  whereAreYou() {
    console.log(`${this.name} is at ${this.isCurrentlyAt}`)
  }

  acceptRoyalInvitiation() {
    // Inline the regular function
	function attendFunction(name) {
	// this is now the global `Window` object
	  console.log(`${name} is at`, this)
	}
	attendFunction(this.name)
  }
}

const cinderella = new FairytaleCharacter('Cinderella')

cinderella.whereAreYou()
// Cinderella is at home
cinderella.acceptRoyalInvitiation()
// Cinderella is at undefined
An example of a regular function inline with a regular function

To summarize, regular functions transport you to a different place where the this is now different. Just like Cinderella going to the function at the palace, her surroundings are now different.

'this' and arrow functions

Arrow functions were a relatively recent addition to JavaScript and make one fundamental difference. When you call it, it doesn't change the value of this at all.

class RandomNameGenerator {
  constructor() {
    this.firstnames = [
        'Debra',
        'Raymundo',
        'Argelia',
        'Jessie',
        'Brigette'
    ]
    
    this.lastnames = [
        'Burnes',
        'April',
        'Menard',
        'Stookey',
        'Pettiway'
    ]
  }

  getRandomName() {
	// Use arrow functions to get a random firstname and lastname
    const randomFirstname = () => {
	  return this.firstnames[
          Math.floor(Math.random() * this.firstnames.length)
      ]
    }

    const randomLastname = () => {
      return this.lastnames[
          Math.floor(Math.random() * this.lastnames.length)
      ]
    }

    return `${randomFirstname()} ${randomLastname()}`
  }
}

const randomNameGenerator = new RandomNameGenerator()
console.log(randomNameGenerator.getRandomName())
// Brigette Menard
Demonstrating this using arrow functions in a random name generator program

In this example, we create two arrow functions, randomFirstname and randomLastname. Since they are created inside the method of a class instance, then their behavior is to preserve the this reference.

So to remember this, imagine the following:

A picture of an arrow being fired to symbolize an arrow function where your surroundings remain the same
You are a skilled archer and your function is to attack the enemy who is approaching. As you fire your arrow, you observe that your surroundings are still the same. Now this is the same as it was before firing your arrow.

So remember the following, with arrow functions, it's like firing an arrow, but your context of this remains the same because you have not moved.

I felt it was important to draw a visual representation of what is happening so it is easier to remember. Regular functions are like going to social functions, your context changes because you go there. Arrow functions are like firing arrows and your context remains the same because you stay where you are.