DEV Community

The "this "- keyword πŸ€” 🀯

Mastering JavaScript fully is a lengthy journey. The this keyword is a very important concept in JavaScript, and also a particularly confusing one to both new developers and those who have experience in other programming languages.
In JavaScript, this is a reference to an object. The object that this refers to can vary, implicitly based on whether it is global, on an object, or in a constructor, and can also vary explicitly based on usage of the Function prototype methods bind, call, and apply.

You may have come across this on your journey as a JavaScript Developer. When I started out, I first saw it when using eventListeners and with jQuery. Later on, I had to use it often with React and I am sure you will do as well. The question is how to fully take control of it.

Explaining this can lead to a lot of confusion πŸ˜•, simply by the naming of the keyword.

πŸ›‘ ⚠️ Important to remember is that thisis tightly coupled to what context you are in, in your program. Let’s start all the way at the top. In our browser, if you just type this in the console, you will get the window-object, the outermost context for your JavaScript. In Node.js, if we do:

console.log(this)

Enter fullscreen mode Exit fullscreen mode

we end up with{}, an empty object. This is a bit weird, but it seems like Node.js behaves that way. If you do

(function() {
  console.log(this);
})();
Enter fullscreen mode Exit fullscreen mode

You will receive the global object, the outermost context. In that context setTimeout , setInterval , are stored. Feel free to play around a little bit with it to see what you can do with it. As from here, there is almost no difference between Node.js and the browser. I will be using window. Just remember that in Node.js it will be the global object, but it does not really make a difference.

To understand this keyword, only we need to know how, when and from where the function is called, does not matter how and where function is declared or defined.

πŸ›‘ Remember: Context only makes sense inside of functions

There are four main contexts in which the value of this can be implicitly inferred:

  • the global context
  • as a method within an object
  • as a constructor on a function or class
  • as a DOM event handler

Global

In the global context , this refers to the global object. When you're working in a browser, the global context is window. When you're working in Node.js, the global context is global.

For example you write a program without nesting anything in functions. You would simply write one line after another, without going down specific structures. That means you do not have to keep track of where you are. You are always on the same level.

When you start having functions, you might have different levels of your program and this represents where you are, what object called the function.

You'll almost always use this in the context of a function, but just remember that if this is used in the global context, then it points to the global object (e.g.: window in the browser and global in Node.js).

The value of this differs depending on how a function is invoked (the call site), so we can’t know the value of this just by looking at the function itself, but we need to know the context in which the function is invoked.

πŸ›‘ Important is to keep track of the caller object
Let’s have a look at the following example and see how this changes depending on the context:

const cake = {
  strong: delicious,
  info: function() {
    console.log(`The cake is ${this.strong ? '' : 
'not '}delicious`)
  },
}

cake.info() // The cake is delicious
Enter fullscreen mode Exit fullscreen mode

Since we call a function that is declared inside the cake object, the context changes to exactly that object. We can now access all of the properties of that object through this . In the example above, we could also just reference it directly by doing cake.strong . It gets more interesting, when we do not know what context, what object, we are in or when things simply get a bit more complex. Have a look at the following example:


const pastry = [
  {
    name: 'Muffin',
    delicious: true,
    info: function() {
      console.log(`${this.name} is ${this.delicious ? '' : 'not '} delicious.`)
    },
  },
  {
    name: 'Choko Dream',
    delicious: false,
    info: function() {
      console.log(`${this.name} is ${this.delicious ? '' : 'not '} delicious.`)
    },
  },
]

function pickRandom(arr) {
  return arr[Math.floor(Math.random() * arr.length)]
}

pickRandom(pastry).info()
Enter fullscreen mode Exit fullscreen mode

Classes and Instances

Classes can be used to abstract your code and share behavior. Always repeating the info function declaration in the last example is not good. Since classes and their instances are in fact objects, they behave in the same way. One thing to bear in mind is that declaring this in the constructor actually is a prediction for the future, when there will be an instance.

class Cake {
  constructor(strawberry) {
    this.strawberry = !!strawberry
  }
  info() {
    console.log(`This cake is ${this.strawberry ? '' : 'not '}strawberry`)
  }
}

const strawberryCake = new Cake(true)
const chocolateCake = new Cake(false)

strawberyCake.info() // This cake is strawberry
chocolateCake.info() // This cake is not strawberry
Enter fullscreen mode Exit fullscreen mode

An Object Method

A method is a function on an object, or a task that an object can perform. A method uses this to refer to the properties of the object.

const capital = {
  name: 'Berlin',
  yearFounded: 1237,

  describe : function() {
    console.log(`${this.name} was founded in ${this.yearFounded}.`)
  },
}

capital.describe()
Enter fullscreen mode Exit fullscreen mode

In this example, this is the same as capital.

const capital = {
  name: 'Berlin',
  yearFounded: 1237,
  details: {
    symbol: 'bear',
    currency: 'Euro',
    printDetails() {
      console.log(
        `The symbol is the ${this.symbol} and the currency is ${this.currency}.`,
      )
    },
  },
}

capital.details.printDetails()
Enter fullscreen mode Exit fullscreen mode

πŸ€” Another way of thinking about it is that this refers to the object on the left side of the dot when calling a method.

A Function Constructor 🧐 🀐

When you use the new keyword, it creates an instance of a constructor function or class. Function constructors were the standard way to initialize a user-defined object before the class syntax was introduced in the ECMAScript 2015 update to JavaScript. In Understanding Classes in JavaScript, you will learn how to create a function constructor and an equivalent class constructor.

function countryCapital(name, yearFounded) {
  this.name = name
  this.yearFounded = yearFounded

  this.describe = function () {
    console.log(`${this.name} was founded in ${this.yearFounded}.`)
  }
}

const berlin = new countryCapital('Berlin', 1237)

capital.describe()
Enter fullscreen mode Exit fullscreen mode

🧐 In this context, this is now bound to the instance of countryCapital, which is contained in the berlin constant.

A DOM Event Handler

In the browser, there is a special this context for event handlers. In an event handler called by addEventListener, this will refer to event.currentTarget. More often than not, developers will simply use event.target or event.currentTarget as needed to access elements in the DOM, but since the this reference changes in this context, it is important to know.

In the following example, we'll create a button, add text to it, and append it to the DOM. When we log the value of this within the event handler, it will print the target.

const button = document.createElement('button')
button.textContent = 'Click me'
document.body.append(button)


button.addEventListener('click', function (event) {
  console.log(this)
})
Enter fullscreen mode Exit fullscreen mode

Once you paste this into your browser, you will see a button appended to the page that says "Click me". If you click the button, you will see Click me appear in your console, as clicking the button logs the element, which is the button itself. Therefore, as you can see, this refers to the targeted element, which is the element we added an event listenerto.

In a nested object, this refers to the current object scope of the method.

Explicit Context

It is difficult to define exactly when to use call, apply, or bind, as it will depend on the context of your program. bind can be particularly helpful when you want to use events to access properties of one class within another class.
For example, if you want to write a simple game, you might separate the user interface and I/O into one class, and the game logic and state into another. Since the game logic would need to access input, such as key press and click, you would want to bind the events to access the this value of the game logic class.

πŸ›‘ The important part is to know how to determine what object this refers to, which you can do implicitly with what you learned in the previous sections, or explicitly with the three methods you will learn next.

Apply and call

They both do basically the same thing, only the syntax is different. For both, you pass the context as first argument. apply takes an array for the other arguments, while call simply separate other arguments by comma.
What do they do? Both of these methods set the context for one specific function call. When calling the function without call , the context is set to thedefault context (or even a bound context). Here is an example:

class Salad {
  constructor(type) {
    this.type = type
  }
}

function showType() {
  console.log(`The context's type is ${this.type}`)
}

const fruitSalad = new Salad('fruit')
const greekSalad = new Salad('greek')

showType.call(fruitSalad) // The context's type is fruit
showType.call(greekSalad) // The context's type is greek

showType() // The context's type is undefined
Enter fullscreen mode Exit fullscreen mode

The context of the last showType() call is the outermost scope, window . Therefore, type is undefined, there is no window.type

call and apply are very similarβ€”they invoke a function with a specified this context, and optional arguments. The only difference between call and apply is that call requires the arguments to be passed in one-by-one, and apply takes the arguments as an array.

One more example in which, you have to create an object, and create a function that references this but has no this context.

const book = {
  title: 'The Lost Symbol ',
  author: 'Dan Brown',
}

function summary() {
  console.log(`${this.title} was written by ${this.author}.`)
}

summary()
Enter fullscreen mode Exit fullscreen mode

Since summary and book have no connection, invoking summary by itself will only print undefined, as it's looking for those properties on the global object.

Top comments (1)

Collapse
 
bhagatparwinder profile image
Parwinder πŸ‘¨πŸ»β€πŸ’»

Excellent post Irene. One key part is missing.

β€œSince we call a function that is declared inside the cake object, the context changes to exactly that object. We can now access all of the properties of that object through this”

It is true as long as info is a function expression. If it turns into an arrow function it’s not true anymore. How this behaves with arrow functions is critical as more developers focus on ES6 and above.

I would do a shameless plug here:

dev.to/bhagatparwinder/arrow-funct...

And if someone needs a different perspective on this keyword (even though Irene has done an excellent job with her examples):

dev.to/bhagatparwinder/this-keywor...