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, …}
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'
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
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
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