In JavaScript the this keyword refers to an object. This is always true unless it is used inside a function when JS is executing in strict mode, in which case it is undefined.

Let’s explore non-strict mode.

If we log this on the console, we can see that this is pointing to the window object:

this
> Window {0: global, window: Window, self: Window, document: document, name: '', location: Location, …}

When you are executing JS in a browser, the global object is the window object . It refers to the open window in the browser. When we log window to the console, we can see it’s the same as this above:

window
>Window {0: global, window: Window, self: Window, document: document, name: '', location: Location, …}

So this points to window when it is called in the global context.

The this Keyword and Context

What this refers to changes depending on the context in which it is being used. Used in a function, this also refers to the global object:

function whatIsThis() {
  console.log(this);
}

whatIsThis();

>Window {0: global, window: Window, self: Window, document: document, name: '', location: Location, …}

Yep, that’s right, when I call the whatIsThis() function, it logs out the same old window object from before.

However, if we use this in an object, it refers to the object itself:

let obj = {
  name: 'demo object',
  location: {
    x: 1,
    y: 3,
  },
  whatIsThis: function() { // <= my whatIsThis function
    console.log(this);
  }
}

obj.whatIsThis();

>{name: 'demo object', location: {…}, whatIsThis: ƒ}

Here I am just putting my whatIsThis function from above into an object literal and calling the function using dot notation.

In the output we can see that this is pointing to the object literal that encloses the function. You can see all the members of the obj object in the output – name, location and the whatIsThis function itself.

It’s important to know that this is happening because whatIsThis is a member of the object.

this in Constructor Functions

What if I use this in a constructor function? Let’s make beer!

function Beer(brand, type, flavour) {
  this.brand = brand;
  this.type = type;
  this.flavour = flavour;
  
  console.log(this); // <= logging out this to the console
  
}

const beer1 = new Beer('Coopers','Pale Ale','Delicious!');

>Beer {brand: 'Coopers', type: 'Pale Ale', flavour: 'Delicious!'}

Here, this is pointing to the new beer object that has been created with the Beer constructor function.

When you create an object using a constructor function, you use the new keyword. What JS does under the hood when you ‘new up’ an instance of an object is create a new empty object, like an empty string literal:

{}

Then it points this at that object. That is why we can use this inside the constructor function to create the object’s properties. We use dot notation – we’ve created brand, type and flavour, eg:

this.flavour = flavour;

When used in an object, this points at the object itself.

When this is not this – Callbacks

There is one scenario in which this seems to be in the context of an object, but it’s actually not, and it might seem confusing.

In my beer function from before, I’m going to add an ingredients property which is an array and then create a method to display those ingredients. I’ll use the map method to enumerate the array items:

function Beer(brand, type, flavour, ingredients) {

  this.brand = brand;
  this.type = type;
  this.flavour = flavour;
  this.ingredients = ingredients;

  this.listIngredients = function() {
    this.ingredients.map(function(ingredient) {
      console.log(ingredient);
    })
  };
    
  
}

const beer1 = new Beer('Coopers','Pale Ale','Delicious!',['Malted barley', 'Hops', 'Water', 'Yeast']);

console.log(beer1.listIngredients());

>Malted barley
>Hops
>Water
>Yeast

Great, my listIngredients function is displaying the beer ingredients.

The first parameter to the map method is a callback, in this case, function(ingredient) {…} . Let’s find out what this looks like within that callback function. I’m adding to the console log the this keyword:

function Beer(brand, type, flavour, ingredients) {

  this.brand = brand;
  this.type = type;
  this.flavour = flavour;
  this.ingredients = ingredients;

  this.listIngredients = function() {
    this.ingredients.map(function(ingredient) {
      console.log(this, ingredient); // <= adding this keyword to console log
    })
  };
    
  
}

const beer1 = new Beer('Coopers','Pale Ale','Delicious!',['Malted barley', 'Hops', 'Water', 'Yeast']);

console.log(beer1.listIngredients());

>Window {window: Window, self: Window, document: document, name: '', location: Location, …} 'Malted barley'
>Window {window: Window, self: Window, document: document, name: '', location: Location, …} 'Hops'
>Window {window: Window, self: Window, document: document, name: '', location: Location, …} 'Water'
>Window {window: Window, self: Window, document: document, name: '', location: Location, …} 'Yeast'

Wow, this is pointing at the window object! Why is that? It’s because that callback function is not a member of the beer1 object – it’s a callback to the map method. That’s in contrast to the listIngredients function, which is a member of the object and therefore I can use this inside that function to point to ingredients and map it. (If you remember from before, the reason this points to the enclosing object is because it is within the context of a member of that object).

Conclusion

So that’s how this behaves in various different contexts. It’s kind of confusing but I think the best way to remember it is some fundamental ideas (keeping strict mode aside):

  1. The this keyword always points to an object
  2. If it’s used in the global context, it points to the window object
  3. If it’s used within an object literal or a constructor function it points to the resulting object
  4. If you use a callback inside a method, that callback is not a method of the object, so this will point to the window object

Afterthought

Doesn’t all this make you feel like listening to Faith No More?

What is this? JavaScript

Leave a Reply

Your email address will not be published.