DEV Community

Cover image for This is it! you got 'this'
Phillip Shim
Phillip Shim

Posted on • Updated on • Originally published at shimphillip.netlify.com

This is it! you got 'this'

 

this keyword must be one of the most confusing JavaScript concepts due to its dynamic nature of how it's invoked. If you find yourself reading this article, you probably have come across this several times and want to have a better understanding. I hope to make this crystal clear with practical examples and minimal jargon. Make sure you open up your console in your favorite browser and follow along!


 

this as global

The most straightforward way to identify this is when it's used as a global object. A global object is an object which can be accessed from anywhere in your JavaScript code. Browsers call this global object window and NodeJS call it global. This basic default binding is called default binding.

console.log(this) // window
console.log(window) // window
console.log(this === window) // true

Did you know fetch and localStorage are part of the global window object?

Variables declared with var keyword and function declarations become properties of the window object. The properties of the global object can be accessed without dot or bracket notation. However, variables declared using let or const keywords don't become part of the global object, instead they will be stored inside an inaccessible environment.

var age = 29
console.log(age) // 29
console.log(this.age) // 29
console.log(window.age) // 29

function sayHi() {
  console.log("Hi")
}
sayHi() // Hi
this.sayHi() // Hi
window.sayHi() // Hi

const sayHello = function() {
  console.log("Hello")
}
sayHello() // Hello
this.sayHello() // Uncaught TypeError: this.sayHello is not a function
window.sayHello() // Uncaught TypeError: window.sayHello is not a function

 

this in functions

this inside regular functions refer to the global object as well. Alternatively, we can also say that the context of these functions is the global object. Context simply means the value of this at a given moment when your code is being run by the javascript engine (This is also known as the 'execution context').

var whatIsThis = function() {
  console.log(this)
}

whatIsThis() // window

Word of caution: when use strict mode is used, it won't allow the default binding of this to the window object. Therefore, the value of this results in undefined.

"use strict"

var whatIsThis = function() {
  console.log(this)
}

whatIsThis() // undefined

 


 

this in methods

Method means it's a function inside an object. this keyword inside methods is set to its parent object. This is called implicit binding because this is bound indirectly to the object it belongs to.

var obj = {
  getThis: function() {
    return this
  },
}

// equivalent ES6 method
var obj = {
  getThis() {
    return this
  },
}

console.log(obj.getThis()) // obj
console.log(obj === obj.getThis()) // true

To access properties of the object inside your methods, you will need to explicitly use this. Otherwise, it will look for the variables with the same name inside the same function scope.

var me = {
  name: "Phillip",
  getName() {
    const name = "Sunnie"
    console.log("My name is " + this.name)
    console.log("My name is " + name)
  },
}

me.getName() // My name is Phillip
// My name is Sunnie

 

me.getName() gives expected strings. What if we assign the definition of our getName method to a variable outside the method? This will cause loss of implicit binding of this because the new getName function is no longer bound to the 'me' object. Instead, because our new getName is declared with var keyword, it's bound to the global window object and will try to search this.name as a property of the global object. Here is what official MDN doc says about this matter:

In most cases, the value of this is determined by how a function is called. It can't be set by assignment during execution, and it may be different each time the function is called.

var me = {
  name: "Phillip",
  getName: function() {
    console.log("My name is " + this.name)
  },
}

var getName = me.getName
me.getName() // My name is Phillip
getName() // My name is undefined

Instead of seeing 'My name is undefined', you might get 'My name is '. That's because we previously used this.name globally so its key is there but its value got set to an empty string

How about we extract the getName method out of the 'me' object and make it a standalone function. Then create another same-named getName property inside the 'me' object and assign the standalone getName function as a reference. Let's try calling them separately. If we call the standalone function by itself, as you observed previously, this will refer to the global object and tries to search the name from window object. If you call the function as a property of the 'me' object, the context of this will be the 'me' object.

function getName() {
  console.log("My name is " + this.name)
}

var me = {
  name: "Phillip",
  getName: getName,
}

getName() // My name is undefined
me.getName() // My name is Phillip

Rule of thumb: Look at the left side of your method which it's called, this belongs to that object. If there isn't any, this belongs to the global object.


 

this using call, apply and bind.

We want to make our 'getName' function more reusable. Let's improve our code by using call, apply and bind functions. These are special functions that append to function definitions and directly invoke them. call and apply take objects as the first argument thus the methods will understand how to handle this. If no argument is passed, the context will be the global object.

function getName() {
    console.log("My name is " + this.name)
}

var me = {
  name: "Phillip",
}

var you = {
  name: "Sunnie"
}

getName.call(me) // My name is Phillip
getName.apply(you) // My name is Sunnie
getName.call() // My name is undefined

The difference between call and apply is how you can pass in several arguments to the methods after the first argument. call will separate the values by commas and apply will also separate the values by commas but inside an array.

function getInfo(age, hobby) {
    console.log(`My name is ${this.name}, I am ${age} and my hobby is ${hobby}`)
}

var me = {
  name: "Phillip",
}

var you = {
  name: "Sunnie"
}

getInfo.call(me, 29, "coding") // My name is Phillip, I am 29 and my hobby is coding
getInfo.apply(you, [28, "floral design"]) // My name is Sunnie, I am 28 and my hobby is floral design

Helpful tip: Comma takes in commas and Apply takes in an array

Often, we want to only associate certain methods with certain objects. bind helps us link a specific method to a specific object so that the value of this is predictable and can be found out by looking at its definition instead of investigating how it's invoked. Unlike call and apply, bind won't immediately invoke its attached function. This is called explicit binding

function getName() {
  console.log("My name is " + this.name)
}

var me = {
  name: "Phillip",
}

getName = getName.bind(me)
getName() // My name is Phillip

Don't forget to reassign it back to the method after you use bind!

The practical use of bind is when a function is passed as a callback. Let's look at one example without the use of bind.

var me = {
  name: "Phillip",
  getName: function() {
    console.log("My name is " + this.name)
  },
}

function calleMeMaybe(callback) {
  callback()
}

calleMeMaybe(me.getName) // My name is undefined

getName is undefined because, under the hood, this is what happens.

callback = me.getName

Let's now bind getName to the 'me' object.

var me = {
  name: "Phillip",
  getName: function() {
    console.log("My name is " + this.name)
  },
}
me.getName = me.getName.bind(me)

function calleMeMaybe(callback) {
  callback()
}

calleMeMaybe(me.getName) // My name Phillip

 

this in function constructors

In JavaScript, functions can serve as constructors to build new objects using the "new" keyword. this will be set to an instance (or an object) that is created by the function constructor.

function Song(author) {
  this.author = author
  this.song = "Let it go"
  this.getInfo = function() {
    console.log(`${this.author} sang ${this.song}`)
  }
}

var song = new Song("Idina Menzel")
song.getInfo() // Idina Menzel sang Let it go

var song1 = new Song("Demi Lovato")
song1.getInfo() // Demi Lovato sang Let it go

// change of context
var getInfo = song1.getInfo 
getInfo() // undefined is sung by undefined

 

this with classes

Classes are modern function constructors. Inside classes, this will behave identically as the function constructors and will refer to particular objects that are created by the classes. Notice that "strict mode" is applied to classes by default.

class Song {
  constructor(author) {
    this.author = author
    this.song = "Let it go"
  }

  getInfo() {
    console.log(`${this.song} is sung by ${this.author}`)
  }
}

const song = new Song("Idina Menzel")
song.getInfo() // Idina Menzel sang Let it go

const song1 = new Song("Demi Lovato")
song1.getInfo() // Demi Lovato sang Let it go

// change of context
const getInfo = song1.getInfo
getInfo() // Uncaught TypeError: Cannot read property 'song' of undefined

If you are familiar with React, you will notice we explicitly bind event handlers with the class itself. That is because event handler methods are passed as callbacks by some event listeners like onClick, onSubmit, etc... Remember, the callback functions lose their context.

class Form extends React.Component {
  constructor(props) {
    super(props)
    this.handleClick = this.handleClick.bind(this) // necessary
  }

  handleSubmit(event) {
    // Handle logic
  }

  render() {
    return <button onClick={this.handleSubmit}>Submit</button>
  }
}

 

this in arrow functions

JavaScript can get quirky and cause unexpected behaviors. What do you think the value of this will be in a function inside of a method? Our intuition tells us it will still refer to the parent object that the method belongs to. However, it doesn't. this actually will refer to the window object. Let's take our prior example and modify it.

var me = {
  name: "Phillip",
  getName: function () {
    function innerFunction() {
      console.log("My name is " + this.name)
    }
    innerFunction();
  },
}

me.getName() // My name is undefined

There are multiple ways to fix this problem.

  1. We can store this to a variable and reference the variable in the inner function. By convention, this variable is called 'self'.
  2. We can use bind inside the method to connect the inner function to the method's context.
  3. Or use the arrow function.

In arrow functions, this retains the value of the enclosing lexical context's this. - MDN

It means that the value of this is set to the function that contains the arrow function.

// 1
var me = {
  name: "Phillip",
  getName: function () {
    var self = this;
    function innerFunction() {
      console.log("My name is " + self.name)
    }
    innerFunction();
  },
}

me.getName()

// 2
var me = {
  name: "Phillip",
  getName: function () {
    function innerFunction() {
      console.log("My name is " + this.name)
    }
    innerFunction = innerFunction.bind(this);
    innerFunction();
  },
}

me.getName()

//3
var me = {
  name: "Phillip",
  getName: function () {
    const innerFunction = () => {
      console.log("My name is " + this.name)
    }
    innerFunction();
  },
}

me.getName()

 


this with HTML

JavaScript event listeners get access to this when an event has been triggered. this then will refer to the HTML element that caused it. If the event listener callback function is declared as an arrow function, this will refer to the window object, its enclosing context.

<h1 class="h1">Hello World</h1>
<h2 class="h2">Hi World</h2>
const h1 = document.querySelector(".h1")
const h2 = document.querySelector(".h2")

h1.addEventListener("click", function(e) {
  console.log(e.target) // <h1 class="h1">Hello World</h1>
  console.log(this) // <h1 class="h1">Hello World</h1>
})

h2.addEventListener("click", e => {
  console.log(e.target) // <h2 class="h2">Hi World</h2>
  console.log(this) // window
})

 


Summary

Thank you for reading my article! 👏👏 I sincerely hope that it was helpful content to demystify any confusion you had with this keyword. I will leave with a summary of my lengthy article. Thanks again!

  • this generally refers to the global object by default and in plain functions: default binding.
  • in strict mode, this is undefined.
  • Inside methods, this is the object which owns the method, but it depends on how this is invoked. Look at the left side of this to determine its value.
  • Use call, apply to explicitly call the methods with desired objects. Use bind to glue together this to a specific object.
  • In Arrow functions, look at the enclosing context to determine the value of this

 

Top comments (4)

Collapse
 
jakehjung profile image
jakehjung

Excellent article Phillip. When I was getting into JavaScript, I read an article somewhere that mentioned that 'this' is used to refer to the antecedent in its 'context', just like English(or any other languages that use pronoun). Thinking this way made so much easier to code in JavaScript. Hope this helps.

Collapse
 
shimphillip profile image
Phillip Shim

Thanks Jake! that is an useful way of thinking about how 'this' works! Nice tip :)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.