This is the Thing About JavaScript

Posted by Bryce Whittington on February 15, 2021

Today, on my mission to understand JavaScript even better, I’m going to try my best to explain the this keyword. I’ve been studying this since my first Flatiron JavaScript lesson, because it caused me quite a few errors in my final two projects (and some still.) It’s one of those things that seems so simple when I read blogs or watch videos about it, but then still trips me up when I least expect it. Hopefully my research can bring you some confidence and clarity in your coding, or at least can point you in the right direction.

buckle up

Look for the this differentiation, because things can get confusing saying ‘this’ so much.

What is this?

this is relatively common in different programming languages, with different rules, but JavaScript was my first experience working with it. I’ll be explaining it from the perspective of someone with my background rather than a different language’s perspective.
The first thing to understand before diving into this is that for every function call, the JavaScript engine creates a new function execution context. With that out of the way, this becomes a little big clearer: this is the reference to the current execution context of every JavaScript function. Functions in JavaScript are basically objects, and like objects, they have their own properties- one of which is this. In other words, this tells us where, and when, and how the function is called (not declared.)

Using this

Practically using this is trickier than understanding the concept of it, to me. Since the value of this is based on the current execution context, it can (and will) change depending on how its surrounding function is defined and invoked.

By default, the execution context of an execution is global, which means this refers to a global object (window in your browser.)

function someFunction() {
    console.log(this === window);
}
someFunction() // true

*An exception to this is if strict mode is enabled, which can prevent anything from being declared on the global object and can make the value of this undefined.

More Examples

function Person(first, last) {
    this.firstName = first;
    this.lastName = last;
    this.fullName = function() {
        console.log(`Hi, my name is ${this.firstName} ${this.lastName}!`);
    }
}
let john = new Person("John","Smith");
john.fullName() // Hi, my name is John Smith!
let lisa = new Person("Lisa","Jones");
lisa.fullName() // Hi, my name is Lisa Jones!

In the example of john.fullName(), this refers to a specific instance of john and then in lisa.fullName(), this refers to a specific instance of lisa, which is different instance of Person.

Method Invocation:

let car = {
    make: "Jeep",
    model: "Liberty",
    description: function() {
        console.log(`${this.make}  ${this.model}`);
    }
}
car.description()  // Jeep Liberty

In this example, the this object in the description() method references the car object.

Call() and Apply()

This is where a little bit of relief comes in with functions. Every function has the call, apply, and bind methods, which can be used to set a custom value for this in the function’s execution context.

function Person(first, last) {
    this.firstName = first;
    this.lastName = last;
    this.fullName = function() {
        console.log(`Hi, my name is ${this.firstName} ${this.lastName}!`);
    }
}
let john = new Person("John","Smith");
john.fullName() // Hi, my name is John Smith!
let lisa = new Person("Lisa","Jones");
lisa.fullName() // Hi, my name is Lisa Jones!
john.fullName.call(lisa) // Hi, my name is Lisa Jones!

You can see here that we can choose to set the value of our john variable to be the lisa object by borrowing its this value. The difference between call() and apply() is essentially how arguments are passed in, with apply() accepting the arguments as an array.

Bind()

function Person(first, last) {
    this.firstName = first;
    this.lastName = last;
    this.fullName = function() {
        console.log(`Hi, my name is ${this.firstName} ${this.lastName}!`);
    }
}
let john = new Person("John","Smith");
john.fullName() // Hi, my name is John Smith!
let lisa = new Person("Lisa","Jones");
lisa.fullName() // Hi, my name is Lisa Jones!
let showLisa = john.fullName.bind(lisa) // Hi, my name is Lisa Jones!

Bind() returns a copy of the function rather than immediately executing the function like call() and apply do. When invoked bind() has the this set to the provided value. This is useful for when the function is executed later, you can know the context for this is correct.

Conclusion

Okay, so this may or may not be more clear to you after reading this. Again, there are so many resources out there (I’ll link some down below) that give clear definitions and examples. Generally, as I’ve studied this, the sites I’m reading tend to give specific examples of different contexts and methods being used. My best advice is to read as many of those as possible to just see lots of real world examples. I know that I learned even more from specific examples as I studied (and continue to study) to write this, so I hope to be able to point you toward something that will make it even clearler in your studying.

Resources

  • MDN for all four of these
  • I found this codeburst article by NC Patro particularly helpful
  • freeCodeCamp.org always puts out very helpful resources
  • I didn’t even go into this with arrow functions here, but this article by Pavan is very thorough for multiple this contexts.