.this keyword in JavaScript
Tags: javascript, beginners, programming, openai
Published: 2023-01-14 17:21:16

My goal in writing this post is to promote a better understanding of `this` keyword by sharing aspects of `this` that I discovered while learning JavaScript. Skip the specifics and jump to the [Cliffs Notes](#cliffs-notes) if you'd prefer.

## Contents
* [Why this exists](#why-this-exists)
* [How this works](#how-this-works)
* [What defines this](#what-defines-this)
* [Function Methods](#function-methods)

## Why _this_ exists

In JavaScript, the `this` keyword is a means to refer to the object or context that the code is currently executing within. In object-oriented programming, `this` keyword makes it possible to reference the current object in callback functions and event handlers, allowing access to and manipulation of the current object's properties and methods. Additionally, `this` enables programmers to build methods that preserve the context of the object they were called on while being reused at different instances.

The `this` keyword can also be used to develop closures, inner functions that have access to the variables and information in their containing functions. It serves as a method to refer to the object whose property the function is, or to the object which is carrying out the current function.

## How _this_ works

In JavaScript, the value of `this` keyword within a function is typically determined by _**how**_ the function is invoked, not by the location of where the function was declared. Except for arrow functions, which have a different syntax and behaviour than regular functions. 

The `this` keyword can be passed to a function as an argument, or it can be implicitly set depending on the execution context.  

The [call()](#call-method), [apply()](#apply-method), and [bind()](#bind-method) methods can also be used to explicitly set the value of `this` when a function is called. Each method can be used to call a function and modify _**how**_ it is invoked, but they work in slightly different ways.

A few of the common factors that dictate how the `this` keyword is determined in different contexts will be discussed next. Then I will discuss the function methods in greater detail. 

## What defines _this_

The context of the code that is presently running determines what the `this` keyword represents. The call-site (the place in your code where a function is called) of the function is what determines the default behaviour of the `this` keyword. 

The following are often the most important factors to consider when defining `this` keyword as a JavaScript programmer:

### 1st Factor 

In the global scope, by default, `this` refers to the global object, also known as the `window` in the browser or `global` in Node.js.

### 2nd Factor

In a regular function call, `this` refers to the global object.

```jsx
function myFunc() {
  console.log(this);
}
myFunc(); // logs the global object (window or global)
```  
### 3rd Factor

In a method call, `this` refers to the object on which the method is defined.

```jsx
let object = {
  myMethod: function () {
    console.log(this);
  }
};
object.myMethod(); // logs object
```
### 4th Factor

In a constructor function, `this` refers to the new object being constructed.

```jsx
function MyConstructor() {
  this.myProp = 'hello';
  console.log(this);
}
let myObj = new MyConstructor(); // logs the new object, with a myProp property
```

### 5th Factor

When a function is called as a method of an object, `this` refers to the object within the function.

```jsx
// First Object
let object = {
  myProp: 'Netflix',
  getMethod: function() {
    return this.myProp;
  },
};
// Second Object
let otherObject = {
  myProp: 'Disney'
};
object.getMethod(); // logs 'Netflix'
otherObject.getMethod (); // logs 'Disney'
console.log(object.getMethod()); // logs 'Netflix'
otherObject.getMethod = object.getMethod // logs f() { return this.myProp }
```

In the above code, the first object is called `object` that has a `myProp` property with a value of `Netflix`, and a `getMethod` method that returns the value of the `myProp` property using `this.myProp`.

When the `object.getMethod()` method is called, `this` inside the method refers to the `object` on which the method is defined, so `this.myProp` evaluates to `object.myProp` which is `Netflix`. Alternatively, `getMethod()` can be called on the second object called `otherObject` to return it's `myProp` property as `Disney`.

> Clear as mud, right!?

The `this` keyword is dynamic and its value is determined implicitly by the execution context of _**how**_ the function is called. 

However, for arrow functions, the value of `this` is not dynamic, it's determined by the scope where the arrow function was defined. Instead of having its own `this` keyword, the arrow function must access `this` from the parent scope. 

> I plan on posting about _arrow functions_ soon to better understand their atypical nature, so stay tuned!

Function methods make JavaScript more predictable by enabling the explicit definition of the value of the `this` keyword. The `call()`, `apply()`, and `bind()` methods allow you to explicitly specify the value of `this` when a function is called. These methods modify **_how_** functions get invoked, but they work in slightly different ways.

The `call()` and `apply()` methods invoke the function immediately, while the `bind()` method returns a new function with `this` bound to a specified value. This new function can be called later.

> Let's take a closer look!

## Function Methods

The following Booking Flights code snippet is from Jonas Schmedtmann's The Complete JavaScript Course 2023, Section 10: A Closer Look at Functions, available on Udemy. It refers to booking flights with the second-largest European airline group and has one main object that I'll now discuss in greater detail on function methods.

```jsx
// Booking Flights
// Main Object
const lufthansa = {
    airline: 'Lufthansa',
    iataCode: 'LH',
    bookings: [],
    // book: function() {}
    book(flightNum, name) {
      console.log(
        `${name} booked a seat on ${this.airline} flight ${this.iataCode}${flightNum}`
      );
      this.bookings.push({ flight: `${this.iataCode}${flightNum}`, name });
    },
};
lufthansa.book(239, 'Jonas Schmedtmann');
lufthansa.book(635, 'John Smith');

const eurowings = {
    airline: 'Eurowings',
    iataCode: 'EW',
    bookings: [],
};
const book = lufthansa.book
book(23, 'Sarah Williams') // ERROR

const swiss = {
    airlines: 'Swiss Air Lines',
    iataCode: 'Lx',
    bookings: [],
};

```
If you run this code, you will get the following output:

```jsx
Jonas Schmedtmann booked a seat on Lufthansa flight LH239
John Smith booked a seat on Lufthansa flight LH635
```

The `book` method of the `lufthansa` object is called with the correct value for `this` (i.e. `this` refers to the `lufthansa` object), so the bookings are added to the `lufthansa` object's `bookings` array.

However, when the `book` function is assigned to the `book` variable and then called with the `book(23, 'Sarah Williams')`, the value of `this` is determined by the way in which the function is called, and in this case it is called as a standalone function, not as a method of an object. 

As a result, the value of `this` is the global object (in a web browser, this is the `window` object). This means that the `airline` and `iataCode` properties of `this` object will not be defined, and you will see an error message
logged to the console similar to the following: 

```jsx
Uncaught TypeError: Cannot read property 'airline' of undefined
```
To fix this error, we need to use the `call()` method on the `book` function to properly set the value of `this` keyword to the `eurowings` object. As a result, we get the booking added to the `eurowings` object's `bookings` array. 

## Call Method

Using the `call()` method effectively specifies the value of `this` when calling the `book` function, like below: 

```jsx
// Call Method
book.call(eurowings, 23, 'Sarah Williams');
console.log(eurowings); // logs Sarah Williams booked a seat on Eurowings flight EW23

book.call(lufthansa, 239, 'Mary Cooper');
console.log(lufthansa); // logs Mary Cooper booked a seat on Lufthansa flight LH239 
```
In the above code, the `book` function is defined as a method of the `lufthansa` object, but the `call()` method on the `book` function can set the value of `this` to the `eurowings` object as well.

The `call()` method allows you to invoke a function and specify the value of `this` as well as any arguments that should be passed to the function when it is called, regardless of where the function is defined.

The value of the `this` keyword is determined dynamically based on **how** a function is invoked. It is also possible to explicitly set the value of the `this` keyword within a function by using the `call()` method.

The `call()` method allows you to specify the value of `this` explicitly, rather than relying on the default behavior of the `this` keyword. This can be useful when you want to reuse a function that was defined in one context, but use it in a different context where the value of `this` needs to be different.

## Apply Method

The `apply()` method allows you to call a function with a specified value for `this`, and an array of arguments.  This can be useful when calling a function with a variable number of arguments.

For example, where `flightData` is set to an array:
```jsx
// Apply Method
const swiss = {
  airline: 'Swiss',
  iataCode: 'LX',
  bookings: [],
  book: function(flightNum, name) {
    console.log(`${name} booked a seat on ${this.airline} flight${this.iataCode}${flightNum}`
  );
  this.bookings.push({ flight: `${this.iataCode}${flightNum}`, name });
},
};
// Array as an argument
const.flightData = [583, 'Carlos Santana'];
book.apply(swiss, flightData);
console.log(swiss);
```

The following is logged to the console:
```jsx
Carlos Santana booked a seat on Swiss flight LX583 { airline: 'Swiss', iataCode: 'LX', bookings: [{ flight: 'LX583', name: 'Carlos Santana' }], book: [Function]}
```
In the above code, the `apply()` method is called on the `book` function, and it is passed two arguments: the value of `this` (the `swiss` object) and an array of arguments (`flightData`). The booking is then added to the `swiss` objects `bookings` array.  

The `apply()` method is less commonly used in modern JavaScript because there is a better way to accomplish the same task. 

I'm referring to the spread operator (`...`), which allows you to spread an array of values into individual arguments when calling a function. This can be a more concise and convenient way of calling a function with an array of arguments in comparison to the `apply()` method. 

For example, instead of using the `apply()` method as shown in the previous example, you could use the spread operator like so: 

```jsx
// Spread Operator
const flightData = [583, 'Carlos Santana'];
book.call(swiss, ...flightData);
console.log(swiss); // logs Carlos Santana booked a seat on Swiss flight LX583
```
Invoking the `book` function, by spreading the `flightData` into separate arguments with the spread operator effectively adds `Carlos Santana` to the `swiss` objects `bookings` array!

Because the spread operator is a relatively new addition to JavaScript, it may not be supported in all environments. It is, however, widely supported in modern browsers and JavaScript runtimes, making it a convenient and concise method of calling a function with an array of arguments.

## Bind Method

The `bind()` method creates a new function that, when called, has its `this` keyword set to the provided value,  that you specify. This new function can be called later.

By calling the `bind()` method on the `book` function and pass different values for `this`, you can create new functions with fixed values for `this`. For example: 

```jsx
// Bind Method
const bookEW = book.bind(eurowings);
const bookLH = book.bind(lufthansa);
const bookLX = book.bind(swiss);
```

The `bind()` method returns a new function with the value of `this` set to the specified value. 

In the above example, the `bind()` method is called on the `book` function three times, each time with a different value for `this`, resulting in three new functions: 

-`bookEW`, which has `this` fixed to the `eurowings` object
-`bookLH`, which has `this` fixed to the `lufthansa` object
-`bookLX`, which has `this` fixed to the `swiss` object.

These functions are identical to the `book` function, except the value of `this` is fixed to the value that was passed to `bind()`. You can then call these functions at a later time, and the value of `this` will be fixed to the value that you specified when you created the function with `bind()`. 

The following gets logged to the console: 

```jsx
bookEW(23, 'Steven Williams'); // Adds a booking to the eurowings object's bookings array
bookLH(654, 'Alice Smith'); // Adds a booking to the lufthansa object's booking array
bookLX(583, 'Bob Johnson'); // Adds a booking to the swiss object's bookings array
```
This can be useful when you want to reuse a function in different contexts, but you want the value of `this` to be different in each context. The `bind` method allows you to create new functions with fixed values for `this`, which can be called at a later time. For example: 

```jsx
// Calling a single function for additional flight bookings
const bookEW23 = book.bind(eurowings, 23);
bookEW23('Jonas Schmedtmann');
bookEW23('Martha Stewart');
```
The `bind()` method in this example, was used to create new functions with the `this` value set to either `lufthansa` or `eurowings`, depending on which object is passed as an argument to `bind()`. 

The `bookEW` function is created with the `this` value set to `eurowings`, so when it is called with `(23, 'Steven Williams')`, it logs the message: `"Steven Williams booked a seat on Eurowings flight EW23"`.

The `bookEW23` function is also created with the `this` value set to `eurowings`, but it is already partially applied with the first argument set to `23`. When it is called with a single argument `'Jonas Schmedtmann'`, it logs the message: `"Jonas Schmedtmann booked a seat on Eurowings flight EW23"`. 

When you create a new function using the `bind()` method and partially apply some of its arguments, the resulting function will only have placeholders for the remaining arguments. 

Partial application refers to the process of creating a new function by fixing some of the arguments of an existing function. The new function is then called with the remaining arguments.

For example, in the case of the `bookEW23` function, the `flightNum` argument is already set to `23`, so when you call `bookEW23` function, you only need to provide the `name` argument. The resulting function call will look like this: `bookEW23(name)`.

You can also use the `bind()` method to set the value of multiple arguments at once. For example: 

```jsx
const bookEW23Martha = book.bind(eurowings, 23, 'Martha');
bookEW23Martha(); // logs Martha booked a seat on Eurowings flight EW23
```

Functional programmers often use partial application to code reusable, configurable functions that are simple to adapt for particular use cases. The `bind()` method is one way to implement partial application in JavaScript, but there are other ways as well, such as using higher-order functions or creating a function that returns a partially applied function.

## Cliffs Notes 

[Back to Contents](#contents)

The `call()`, `apply()`, and `bind()` methods are all ways to specify the value of `this` keyword when calling a function.

The `call()` method allows you to call a function with a specified value for `this`, and pass any number of arguments to the function as separate parameters.

The `apply()` method allows you to call a function with a specified value for `this`, and pass any number of arguments to the function as an array.

The `bind()` method allows you to create a new function with a specified value for `this`, and optionally set some or all of the arguments to the new function. The new function can then be called at a later time, with the remaining arguments passed when the function is called.

These methods can be useful when you want to reuse a function in different contexts, but you want the value of `this` or the arguments to be different in each context. They allow you to specify the value of `this` and the arguments explicitly, rather than relying on the default behavior of the `this` keyword and the arguments.
 
Thanks for reading and hopefully this was helpful! I welcome constructive feedback, and if you find an error, please kindly let me know. Disclaimer: This post was produced with the help of artificial intelligence.