What is "this"?
Tags: javascript, frontend, beginners, programming
Published: 2024-04-04 19:53:11

Understanding "this" within JavaScript was one of the most confusing concepts for me to grasp.  The value of "this" can change depending on what context it is called in or what type of function is calling it.  This made it seem unpredictable to me.  Once I grasped a few key concepts, I understand how to apply "this" in my own applications.

## What does "this" actually do?

The "this" keyword will reference an object. The object it will reference depends on the type of function "this" is in, where the function is invoked, or if specific methods (bind, call, or apply) are called.

### Functions

When "this" is within a function (not a method), it will reference the global object by default.

Look at the following example:

```javascript
function thisValue() {
    console.log(this);
}

thisValue();

// The global object is logged
```
If "strict mode" is enabled, "this" will reference undefined instead.

### Methods

A method's "this" value is by default set to the object that called the method.

In this example:
```javascript
const user = {
    firstName: 'Peter',
    lastName: 'Parker',
    getFullName() {
        return `${this.firstName} ${this.lastName}`;
    }
}

console.log(user.getFullName());

//'Peter Parker' is logged
```

"this" within the getFullName method references the user object because it is the object calling getFullName.  The method could be rewritten as:
```javascript
getFullName() {
    return `${user.firstName} ${user.lastName}`;
}
``` 

There is a key difference to understand to fully grasp the concept of "this".  A function has its own "this" value while an arrow function does not. 

Using the previous example but with a method defined with an arrow function:
```javascript
const user = {
    firstName: 'Peter',
    lastName: 'Parker',
    getFullName: () => {
        return `${this.firstName} ${this.lastName}`;
    }
}

console.log(user.getFullName());

//undefined undefined is logged
```
Why is undefined returned?  A method defined with an arrow function will look at the surrounding lexical scope from where it was defined to find a "this" value.  Since the user object is defined in the global scope, the arrow function method will look there for a "this" value, meaning that "this" will reference the global object.  What does this mean?  It means that using methods defined with arrow functions is generally a bad idea because their "this" values will not point to the object calling them.

However, using arrow functions within a method works great!

When used inside of a method:
```javascript
const user = {
    firstName: 'Peter',
    lastName: 'Parker',
    getFullName() {
        const getName = () => {
            return `${this.firstName} ${this.lastName}`;
        }
        return getName();
    }
}

console.log(user.getFullName());

//"Peter Parker" is logged
```
The arrow function is declared inside of the getFullName method, so it will look for a "this" value in the surrounding lexical scope.  The arrow function will use the method's "this" value (the user object) and will work as intended!

### Constructors

Within a constructor function, "this" will refer to the object being created by default. 

Our constructor function looks like this:
```javascript
function Car(color, make) {
    this.color = color;
    this.make = make;
}
```
and we want to make a new object using the constructor:
```javascript
const blueChevy = new Car('blue', 'Chevy');

console.log(blueChevy);

// Car { color: 'blue', make: 'Chevy' } is logged
```

"this.color" is actually "blueChevy.color" and "this.make" is  "blueChevy.make".  What happens if we create another object using the constructor:
```javascript
const redFord = new Car('red', 'Ford');

console.log(redFord);

// Car { color: 'red', make: 'Ford' } is logged
```
"this.color" is now "redFord.color" and "this.make" is now  "redFord.make"!

### bind(), call(), and apply()

When using bind(), call(), or apply(), "this" can be set to a different value than default.  These methods won't work with arrow functions since they don't have their own "this" values.  

#### bind()

Take a look at this example using bind():
```javascript
const user = {
  firstName: 'Peter',
  lastName: 'Parker',
  getFullName () {
      const getName = () => `${this.firstName} ${this.lastName}`
      return getName();
  }
}

const userTwo = {
  firstName: 'Mary',
  lastName: 'Jane'
}

const getUserTwoFullName = user.getFullName.bind(userTwo); 

console.log(getUserTwoFullName());

// "Mary Jane" is logged
```
You are probably wondering "Isn't "this" supposed to point to the object calling it?".  Well, the way bind() works is that it copies the function it is called on.  It then creates a new function that is identical to the old one but with the specified "this" value provided.  Since userTwo was the argument given to bind(), the value of "this" within getUserTwoFullName will always be userTwo.

#### call()

The call() method allows a function or method to be called with a specified this value.  While giving "this" a value, there is an option to provide arguments to the function being called as well.

Here is an example of using call() while just giving "this" a new value:

```javascript
function getFullName() {
    return `${this.firstName} ${this.lastName}`;
}

const userOne = {
    firstName: 'Peter',
    lastName: 'Parker'   
}

const userTwo = {
    firstName: 'Mary',
    lastName: 'Jane'   
}

console.log(getFullName.call(userOne));

// Peter Parker is logged

console.log(getFullName.call(userTwo));

// Mary Jane is logged
```  
As you can see, the function is called with different "this" values depending on what argument call() is given.  This differs from bind() because call() does not create a new function.

Look at this example when call() also provides additional arguments for the function being called:

```javascript
function checkIfSpiderMan(spiderMan) {
    let string = ''
    spiderMan ? string = 'is Spider Man!'                    
              : string = 'is not Spider Man.'
        
    return `${this.firstName} ${this.lastName} ${string}`;
}

const userOne = {
    firstName: 'Peter',
    lastName: 'Parker'   
}

const userTwo = {
    firstName: 'Mary',
    lastName: 'Jane'   
}

console.log(checkIfSpiderMan.call(userOne, true));

// "Peter Parker is Spider Man!" is logged

console.log(checkIfSpiderMan.call(userTwo, false));

// "Mary Jane is not Spider Man." is logged
```  
The second argument given to each call() method in the example above is passed as the argument for the checkIfSpiderMan() function.

### apply()

Now that you know what bind() and call() do, what makes apply() different? Well, apply() is identical to call(), with one exception.  The second argument passed to apply() is always an array that contains the arguments for the function being called by it.

See the slight difference between call and apply below:
```javascript
function checkIfSpiderMan(spiderMan) {
    let string = ''
    spiderMan ? string = 'is Spider Man!'                    
              : string = 'is not Spider Man.'
        
    return `${this.firstName} ${this.lastName} ${string}`;
}

const userOne = {
    firstName: 'Peter',
    lastName: 'Parker'   
}

const userTwo = {
    firstName: 'Mary',
    lastName: 'Jane'   
}

console.log(checkIfSpiderMan.call(userOne, true));

// "Peter Parker is Spider Man!" is logged

console.log(checkIfSpiderMan.apply(userTwo, [false]));

// "Mary Jane is not Spider Man." is logged
``` 

## Wrapping it Up

Once I grasped the concept of "this" I became much more confident reading and writing JavaScript code!