DEV Community

Cover image for Understanding `this` in JavaScript
Karson Kalt
Karson Kalt

Posted on

Understanding `this` in JavaScript

If you're coming to JavaScript from an Object-Oriented language like Ruby or Python, the concept of this in JavaScript might give you a little bit of trouble. It turns out, this in JavaScript is a little more complex than we might think assuming it to work similar to self.

Let's take a step back and think about self in an OO language. We know self will always to the current instance of a class, and that when defining a class method, this refers to the class itself.

What's different in JavaScript?

Let's first back up and define what type of language JavaScript is. Yes, we can create "objects" in JavaScript, but it is only through prototypal inheritance.

In an object method in JavaScript, this refers to the object invoking the function, not the instance of an object.

Let's try the following code in the console:

function whatIsThis() {
  return this;
}

whatIsThis()
// => Window {window: Window, self: Window, document: document, name: '0.980485403589378', location: Location, …}
Enter fullscreen mode Exit fullscreen mode

The return value is the global object, in the console, this is the Window object. Simply put, what object is invoking our function whatIsThis, it is the global object, where all of our other code lives.

Let's try dig a little deeper and start building an object with some functionality. Here, we create a person object and give it a function to return the full name.

const person = {
  firstName: "Karson",
  lastName: "Kalt",
  id: 2948,
  fullName: function() {
    return this.firstName + " " + this.lastName;
  }
};

person.fullName()
// => 'Karson Kalt'
Enter fullscreen mode Exit fullscreen mode

The method .fullName(), is called by the person object, so in this case, this refers to the object person. So far so good right? Well, let's keep exploring, this time invoking a callback function.

In the following example, we have a person object who has an attribute of pets. What if we want to create a method that prints to the console the name of the person and the name of the pet?

const person = {
  firstName: "Karson",
  lastName: "Kalt",
  pets: ["Jazz the Dog", "Maniac the Cat", "Seymour the Turtle"],
  id: 2948,
  allPets: function() {
    this.pets.forEach(function(pet) {
      console.log(this.firstName + " has a pet named "+ pet)
    })
  }
};

person.allPets()
// The console prints the following:
// undefined has a pet named Jazz the Dog
// undefined has a pet named Maniac the Cat
// undefined has a pet named Seymour the Turtle
Enter fullscreen mode Exit fullscreen mode

So why are we able to iterate through each pet, but the firstName attribute becomes undefined. Let's think about a core principle of JavaScript -- hoisting. This is a core part of JavaScript, any of our function declarations are first stored in memory before any code is run. This allows us to use a function before the actual line it appears on, but the function now lives globally. Therefore, the global object is the one executing function(pet), and does not have an attribute defined for firstName.

So, in reality function(pet) is not a method of our person object, the only method assigned to person is allPets.\

A quick and easy way to fix this issue, is to tell the .forEach method what this is referring to, by passing it an optional second argument that defines what this is inside the iterator. Within the scope of this.pets, this refers to the object itself, so we can pass .forEach an argument of this.

const person = {
  firstName: "Karson",
  lastName: "Kalt",
  pets: ["Jazz the Dog", "Maniac the Cat", "Seymour the Turtle"],
  id: 2948,
  allPets: function() {
    this.pets.forEach(function(pet) {
      console.log(this.firstName + " has a pet named "+ pet)
    }, this)
  }
};

person.allPets()
// The console prints the following:
// Karson has a pet named Jazz the Dog
// Karson has a pet named Maniac the Cat
// Karson has a pet named Seymour the Turtle
Enter fullscreen mode Exit fullscreen mode

Since ES6, we quickly create functions that bind this where they are declared, by using an arrow function instead:

const person = {
  firstName: "Karson",
  lastName: "Kalt",
  pets: ["Jazz the Dog", "Maniac the Cat", "Seymour the Turtle"],
  id: 2948,
  allPets: function() {
    this.pets.forEach(pet => {
      console.log(this.firstName + " has a pet named "+ pet)
    })
  }
};

person.allPets()
// The console prints the following:
// Karson has a pet named Jazz the Dog
// Karson has a pet named Maniac the Cat
// Karson has a pet named Seymour the Turtle
Enter fullscreen mode Exit fullscreen mode

Top comments (4)

Collapse
 
aminmansuri profile image
hidden_dude

It's simple "self" is not part of JavaScript.
It just so happens that the DOM has a variable called "self" in the Window object. So if you say self you're saying Window.self.

this is a part of the official JavaScript language. And it works similar to other OO languages. (this is an OO feature)

Collapse
 
aminmansuri profile image
hidden_dude

Smalltalk had a concept of "self" which was "this".
But when they implemented rudimentary OO features into C++ they called it "this" and hence we use "this" instead of "self".

Collapse
 
karsonkalt profile image
Karson Kalt

Very interesting! Thanks for sharing!

Thread Thread
 
aminmansuri profile image
hidden_dude

Yes.. inside a method, "self" would refer to the instance, and "class" refers to the class.

So calling a class method could be accomplished as:

self class foo