How not to be afraid of the 'this' keyword
Tags: javascript
Published: 2020-12-23 17:08:07


#
To start talking about the 'this' keyword, we should first understand where it comes from.
When a function (or method) gets invoked, it has 2 phases: the creation phase, and the execution phase.
#
There are a lot of things to discuss when it comes to those phases, and a lot of concepts like **execution context, lexical environment, variable environment, scope and scope chain** (But don't worry, I will discuss them in depths in the next article). Therefore, for simplicity, in this article, we only need to know that the value of 'this' keyword is **NOT static**.

It depends on **how** the function is called, and its value is only assigned **when** the function is actually called.

#
What do I mean by "It depends on how the function is called"? glad you asked!
In JavaScript, there are different ways in which functions can be called and as a result, the 'this' keyword gets different value:

### 1. **Simple function call:**
In this case, the 'this' keyword points to the global object - the window,
but in 'strict mode' the 'this' keyword will be undefined.
```js
'use strict'
console.log(this); // window

// es5 way for writing function
const calcAgeES5 = function(birthYear) {
  console.log(new Date().getFullYear() - birthYear);
  console.log(this); // undefined (without the 'use strict' - window)
}

calcAgeES5(1991);

// es6 way for writing function
const calcAgeES6 = birthYear => {
  console.log(new Date().getFullYear() - birthYear);
  console.log(this); // window
}

calcAgeES6(1991);
```

### 2. **Method:**
A method is a function attached to an object. In this case the 'this' keyword points to the object on which the method is called, or in other words, it points to the object that is calling the method.


```js
const marry = {
  birthYear: 1988,
  calcAge: function() {
    console.log(this) // marry
    return new Date().getFullYear() - this.birthYear;
  }
}
marry.calcAge();


const joe = {
  birthYear: 2010
}

joe.calcAge = marry.calcAge;
joe.calcAge(); // this => joe
```

In the following example we save the *'calcAge'* method called on *'marry'* to a variable called 'func'. When we will log 'func' we will see the method printed to the console:
*ƒ () {
    return new Date().getFullYear() - this.birthYear;
    console.log(this);
  }*

But, on a strict mode, when we will call *func()*, the 'this' keyword of that execution context will be **undefined** because it's a regular function call
that is not attached to any object. Without the 'use strict' the 'this' keyword will be the **window object**.

```js
'use strict'
const func = marry.calcAge;
console.log(func) // log the function
func(); // this => undefined
```

### 3. **Arrow functions:**
**Arrow functions do not get their own 'this' keyword**, they get the 'this' keyword of the surrounded function (the parent function).
It's called the lexical 'this' keyword because it simply gets picked up from the outer lexical scope.
In the following example, the parent scope is the global scope because the *'marry'* object lives in the global scope, therefore the 'this' keyword is the window.

```js
const marry = {
  firstName: 'Marry',
  birthYear: 1988,
  calcAge: function() {
    console.log(this) // marry
    return new Date().getFullYear() - this.birthYear;
  },
  greet: () => {
    console.log(this); // window
    console.log(`Hello ${this.firstName}`);
  }
}

marry.greet(); // Hey undefined
```

**NOTE:** variables declared with 'var' create properties on the global object, therefore in this case where we declared *firstName* with 'var' and we will call *'marry.greet()'*, we will get *'Hello Tomas'*. It happens because when the *greet* method gets called it searches for 'firstName' variable in its local scope, does not find it, and go up in the scope chain until it finds it on the window object, there we have *window.firstName* because of the declaration with *'var'*.

```js
var firstName = 'Tomas';
marry.greet(); // Hello Tomas
```

The way to fix the problem with the *'greet'* method is to write it in a form of a regular function and not an arrow function.

```js
const marry = {
  firstName: 'Marry',
  birthYear: 1988,
  calcAge: function() {
    console.log(this) // marry - the object that call the method
    return new Date().getFullYear() - this.birthYear;
  },
  greet: function() {
    console.log(this); // marry
    console.log(`Hello ${this.firstName}`); // Hello Marry
  }
}

marry.greet();
```

##### Function inside a method:

```js
const marry = {
  birthYear: 1988,
  calcAge: function() {

    const isMillenial = function() {
      console.log(this.birthYear >= 1981 && this.birthYear <= 1996); // undefined
    }
    isMillenial();
    return new Date().getFullYear() - this.birthYear;
  }
}
marry.calcAge();
```

*'isMillenial'* is a regular function call even though it happens inside of a method, and as we have learned earlier in this article inside a regular function call the 'this' keyword is the global object - window (and undefined in 'use strict' mode). There are 2 solutions for the "problem":

i. Outside the *'isMillenial'* function save the 'this' to a variable:

```js
const self = this; // self or that
const isMillenial = function() {
  console.log(self.birthYear >= 1981 && self.birthYear <= 1996); // true
}
isMillenial();
```

ii. Use arrow function which takes the 'this' of his surrounded environment, which in this case is *'calcAge'* method, which its this' keyword is *'marry'* object

```js
const marry = {
  birthYear: 1988,
  calcAge: function() {

    const isMillenial = () => {
      console.log(this.birthYear >= 1981 && this.birthYear <= 1996); // true
    }
    isMillenial();
    return 2020 - this.year;
  }
}
marry.calcAge();
```

### 4. The 'new' operator
To explain the new operator we first need to understand what is a constructor function.
A constructor function is a function that used as a blueprint to create objects, therefore when we call the function, it must be with the new operator
and when we write a constructor function,  the name should start with a capital letter.

#
Constructor functions are used to stimulate classes which we have now in ES6 but as syntactic sugar.
An arrow function cannot be a function constructor because as I have stated, it does not have its own 'this' keyword.
Only function declaration and function expression can be a constructor function.

```js
const Person = function(firstName, birthYear) {
  console.log(this); // Person {}
  this.firstName = firstName;
  this.birthYear = birthYear;

  // NEVER DO THIS
  this.calcAge = function() {
    console.log(2020 - this.birthYear);
  }
}

const amit = new Person('Amit', 1991);
console.log(amit); // Person {firstName: "Amit", birthYear: 1991}
```
When we call a constructor function with the new operator there are 4 steps that happen  behind the scenes
1. new empty object is created
2. the function is called and 'this' keyword point on the newly created object.
3. the newly created object has a link to the prototype (on our example: Person).
4. the new object created on step 1 returned from the constructor function.

**NOTE:** you should never create a method inside a constructor function because if that function has many methods, each object that builds based on it, would carry around all the methods. Instead, we should use prototype inheritance, but this is a topic for another article.

### 5. call, apply, bind
Help us to set the 'this' keyword manually

```js
const lufthansa = {
  airline: 'Lufthansa',
  iataCode: 'LH',
  bookings: [],
  book(flightNum, name) {
    console.log(`${name} booked a seat on ${this.airline} flight ${this.iataCode}${flightNum}`);
    this.bookings.push({
      flight: `${this.iataCode}${flightNum}`,
      passengerName: name
      })
  }
}
lufthansa.book(239, 'John Lennon');
lufthansa.book(447, 'Amy Winehouse');
```
Now, let's say we have another airline, with different properties but it still needs the book method.
We can copy and paste that method, but it's bad practice. What we should do is to store the method in a variable, so we can
use it in other places. The reason why we can do it like that is that in js functions are first-class citizens.

```js
const book = lufthansa.book();
book(123, 'Marge Simpson'); // Cannot read property airline of undefined
```
Because 'book' is a regular function call, the 'this' keyword points to undefined (in strict mode).

The way to fix it is to tell JS explicitly what the 'this' keyword should be and here come call, apply, and bind.
* **call && apply:** functions that their first argument is the one we want the 'this' keyword to point on.
The other arguments are the argument which the function we call on the call or apply methods takes.
The difference between call and apply is that apply gets the argument as an array (or an array-like object) and 'call' gets them individually.

```js
const elal = {
  airline: 'Elal',
  iataCode: 'EL',
  bookings: []
}

book.call(elal, 123, 'Marge Simpson'); // 'Marje Simpson' books a seat on Elal flight EL123
book.apply(elal, [789, 'Alice Cooper']); // 'Alice Cooper' books a seat on Elal flight EL789
```

* **bind:** also allows us to manually set the 'this' keyword for any function call. The difference is that bind does not immediately call the function, instead it **returns a new function** where the 'this' keyword set to the provided value.

```js
const bookEl = book.bind(elal);
bookEl(123, 'Marge Simpson') // 'Marje Simpson' books a seat on Elal flight EL123

// OR we can provide arguments (partial application)
const bookEl123 = book.bind(elal, 123);
bookEl123('Marge Simpson') // 'Marje Simpson' books a seat on Elal flight EL123
bookEl123('Diana Ross') // 'Dianna Rose' books a seat on Elal flight EL123
```

There are cases when we do not mind what the 'this' keyword is, but we still use bind, for example in a partial application when we preset parameters.
Be aware that the argument you want to preset must be the first argument;

```js
const addTax = (rate, value) => value + value * rate;

const addTax30 = addTax(null, 0.3);
addTax30(200);
```

### 6. **Event listener:**
In an event handler function, the 'this' keyword always points to the DOM element that the handler function is attached to.

```html
<button class="buy">Buy a new plane</button>
```

```js
const lufthansa = {
  airline: 'Lufthansa',
  iataCode: 'LH',
  bookings: []
}

lufthansa.planes = 300;
lufthansa.byPlane = function() {
  console.log(this); // <button class="buy">Buy a new plane</button>
  this.planes++;
  console.log(this.planes); // NaN
}

document.querySelector('.buy').addEventListener('click', lufthansa.byPlane);
```

If we will run this code, the line we log 'this' to the console will give us the reference to the DOM element the handler function is attached to,
therefore at the line, we log Lufthansa's planes to the console we will get NaN.

The way to fix it is to use the bind method because in event listener we don't want to immediately call the function, we just pass a reference to the function
which will be called when the event accrues.

```js
document.querySelector('.buy').addEventListener('click', lufthansa.byPlane.bind(lufthansa));
```

#
**Conclusions:**


The 'this' keyword isn't static. It depends on how the function is called, and its value is only assigned when the function is called.

In this article we have covered many cases when the 'this' keyword gets different values, so to summarize the article I am going to tell you what the 'this' keyword will never be:
* 'this' will never point to the function in which we are using it.
* 'this' will never point to the variable environment of the function.

#
As a side note, I would like to share with you one of the reasons I decided to write the first blog post about the 'this' keyword.
When I started learning JavaScript and going to my first interviews I was asked a lot about the 'this' keyword,
and even I went through that topic before every interview when the interviewer asked me a question about the 'this' keyword,
I got confused and nervous and did not get it right.

#
Thanks for reading, hope you enjoyed and learned something new or at least feel more comfortable with the 'this' keyword now.

😇 Link to the original blog post at my blog: 
https://syntactic-sugar.netlify.app/this