DEV Community

loading...
Cover image for The ultimate guide to understanding the 'this' keyword in JavaScript

The ultimate guide to understanding the 'this' keyword in JavaScript

albert_hadacek profile image Albert Hadacek ・4 min read

To fully understand JavaScript, we have to master several key topics. In my previous articles, I covered prototypical inheritance, iterators/generators, hoisting, promises, and closure. Today, we are gonna look at the this keyword, which confuses a lot of even senior developers and frequently appears in coding interviews.

What the heck is the 'this' keyword?

In JavaScript the keyword this determines how a function is called. It basically represents an object based on the execution context of a function. If we just print this it will refer to the global window object (if we run our code in the browser).


console.log(this) // window

function whatIsThis() {
  return this
}

whatIsThis() // window

function createGlobalVariable() {
  this.name = "Monica"
}

console.log(name) //  "Monica"

Enter fullscreen mode Exit fullscreen mode

Things change when we declare an object with a method on it.


const person = {
  name: "Erlich",
  printName() {
    console.log(this.name)
  }
}

person.printName() // "Erlich"

Enter fullscreen mode Exit fullscreen mode

In the snippet above the this keyword is referencing the object on which we called the function.

The first rule of thumb is. If a function is stored on the object, the this keyword references the object, if a function is declared and called in the global scope, it references the window object.

'this' and the 'new' keyword

If we call a function with the reserved keyword 'new' things get a bit different.


function creator(val) {
  this.val = val
}

creator("Dog")

console.log(val) // "Dog"

const obj = new creator("Dog")

console.log(obj) // {val: "Dog"}

Enter fullscreen mode Exit fullscreen mode

The new does four things for us in the background. It creates an empty object, it assigns this to it, it creates a link to the prototype property and it returns the object. This concept is the core of the OOP design in JavaScript. If you wanna explore it a bit more, read this article:

Strict mode

In ES5 the strict mode was introduced in JavaScript, it prevents us from creating global variables as we can't reference the global object using this in functions.


// Turning the strict mode on
"use strict"

function globalDoesNotWork() {
  return this
}

function globalCreator(name) {
  this.name = name
}

console.log(this) // window
globalDoesNotWork() // undefined 
globalCreator() // TypeError

Enter fullscreen mode Exit fullscreen mode

Implicit object binding

So far, the rules are quite simple. Now, we will get to the more "confusing" stuff.


const obj = {
  name: "Bertram",
  val: this
}

// What is 'this'?
console.log(obj.val) // window

Enter fullscreen mode Exit fullscreen mode

The reason why this refers to the global object is, that we are not using it in a function. Therefore it is not "rebound".

If we have a nested object, the this keyword will always refer to the object on which we have the method.


const person = {
  name: "Dinesh",
  methods: {
    printName() { 
      console.log(this.name)
    }
  }
}

person.methods.printName() // undefined

Enter fullscreen mode Exit fullscreen mode

The methods object does not have any property called name and for that reason we get undefined.

Explicit binding

JavaScript allows us to explicitly bind the keyword this to an object of our choice using methods like call(), apply(), and bind()


const person = {
  name: "Dinesh",
  methods: {
    printName() { 
      console.log(this.name)
    },
    changeName(name) {
      this.name = name
      return this.name
    }
  }
}

person.methods.printName.call(person) // "Dinesh"
person.methods.changeName.call(person, "Richard") // "Richard"
person.methods.changeName.apply(person, ["Jared"]) // "Jared"

person.methods.changeName.bind(person)
person.methods.changeName("Russ") // "Russ"

Enter fullscreen mode Exit fullscreen mode

These methods take the object which this should be bound to as their first argument. Call() and apply() immediately invoke the methods. The only difference between them is how we specify the argument list. Call() accepts them in a form of a "list", but apply() as an array which is then spread. The Bind() method only binds the keyword this but we have to then call the function (this is sometimes used in React's class components).

Arrow functions

In ES6 we received a new addition to the language in form of arrow functions. Despite many beliefs, they are not identical to function declaration as they don't define the this keyword at all. Therefore, it resolves this lexically - going up in the scope to see what is this referring to. It does not even define the arguments array-like object.


const obj = {
  type: "Laptop",
  printType: () => console.log(this.laptop)
}

obj.printType() // undefined - as this refers to window
obj.printType.call(obj) // undefined - objects are not scopes

const obj2 = {
  type: "iPad",
  printType() {
    setTimeout(() => console.log(this.type), 1000)
  }
}

obj.printType()
// 1000ms
// "iPad"

Enter fullscreen mode Exit fullscreen mode

The reason why the ob2.printType() works is that that the arrow function will search for this in its scope and finds it defined in the obj.printType declaration where this equals obj2. If we used a regular function in that setTimout we would have to bind it.

Summary

The this keyword refers to the context where a function is called.

Invoking a function using new will set this to a newly created object, it looks similar to someFunction.call({}, arg).

We can explicitely bind the keyword this using three methods - bind(), apply(), call().

Arrow function do not define their own this.

Discussion (0)

pic
Editor guide