Is this (keyword) a problem?
Tags: javascript, typescript, programming
Published: 2023-04-05 10:00:00

One of the trickiest part of Javascript language is `this` mechanism. It is used to refer to the object that is currently executing the code. 
It is not surprising that questions about `this` are the part of most coding interviews. Let's dive into it step by step. At the end 
I'll show you great set of questions that will help you to evaluate the value of this in any context.

## this in global Context
At first, let's look at the global context. 
```javascript
// in browser
this === global // true
```
Top-level `this` always points to the global object. It is worth to mention that this behavior is the same both for strict mode and non-strict mode.

```javascript
// in node
this === global // true - only within the node REPL

// when excecuting the same code within a node module
this === module.exports // true
```

## this in function calls
Usually the value of a function's `this` is determined by the context in which the function is called. That means the `this`
value may be different each time the function is called.

```javascript
function simpleCall() {
    console.log(this); // global object
}
```
If we are in strict mode, the value of `this` is `undefined` in the global context.
```javascript
"use strict";

function simpleCall() {
    console.log(this); // undefined
}
```
With that knowledge let's assign a property to the global object and check the value of `this` in the function call one more time:
```javascript
function simpleCall() {
    this.name = "John";
    return this.name;
}

simpleCall(); // John
```
This could be a problem in some cases. The following examples shows how we can assign properties to the global object without any control. 
The way to avoid this is to enable a strict mode. That prevents us from accidentally creating global variables, which is a good thing:
```javascript
function simpleCall() {
    this.name = "John"; // Error: cannot set property 'name' of undefined
    return this.name;
}

simpleCall(); // Error: cannot set property 'name' of undefined
```

## this in constructor calls
Here's a simple example of `Coffee` function that accepts and initializes two properties:
```javascript
function Coffee(name, price) {
    this.name = name;  
    this.price = price;
}

const coffee = new Coffee("Cappuccino", 2.5);
console.log(coffee) // undefined
```
You should probably see the problem with the code above. `this` within the function refers to global object because we are not in a strict mode.
Now we have accidentally created two global variables: `name` and `price`. Enabling a strict mode also won't solve this issue.
Since the `this` is set to undefined we cannot assign properties to it - we will get an error - at least we didn't pollute the global object.

There is a little hint about the problem. The function name is uppercased, so we were supposed to use `new` keyword to create an object:
```javascript
function Coffee(name, price) {
    console.log(this) // Coffee {}
    this.name = name;  
    this.price = price;
}

const coffee = new Coffee("Cappuccino", 2.5);

console.log(coffee) // Coffee { name: 'Cappuccino', price: 2.5 }
console.log(global.name) // undefined
console.log(global.price) // undefined
```

In JavaScript, a function call with a new operator in front of it turns it into a constructor call. 
When a function is invoked as a constructor, a brand-new object is created for us automatically, and that new object 
is then used as the `this` binding for the function call.

## this in method calls
It’s common that an object method needs to access the information stored in the object to do its job. To access the object, 
a method can use `this` keyword.

To evaluate the value of `this` in a method call, we need to know the context of the call.  

The value of `this` is the object "before dot", the one used to called the method.

```javascript
const coffee = {
    name: "Cappuccino",
    price: 2.5,
    orderCoffee() {
        console.log(`Can I get ${this.name}, please?`); // Can I get Cappuccino, please? (this === coffee)
    }
}
```

To better illustrate this case let's create the function ad assign it to two different objects:
```javascript
const coffee = {price: 2.5,}
const tea = {price: 1.5} 

function getPrice() {
    console.log(this.price)
}

coffee.price = getPrice;
tea.price = getPrice;

// "this" inside the function is the object that called the method
coffee.price(); // 2.5 (this == coffee)
tea.price(); // 1.5 (this == tea)
```

It is a great moment demonstrate another case with a complex property chain like this below:
```javascript
city.district.cafe.coffee.price(); // 2.5
```
We're still getting the same result. The receiver of the method call is still the most immediate property before the method - which is `coffee`. The
`city.district.cafe` prefix doesn't influence `this` binding.

Real difficulty with understanding `this` behavior in Javascript starts when a method loses its receiver. Going back to our example code:
```javascript
const coffee = {
    name: "Cappuccino",
    price: 2.5,
    orderCoffee() {
        console.log(`Can I get ${this.name}, please?`); // Can I get Cappuccino, please? (this === coffee)
    }
}

const yourOrder = coffee.orderCoffee;
yourOrder(); // Can I get undefined, please? (this === global)
```
I'm sure that you don't want to order undefined 😅. This outcome is caused by the fact that the method loses its receiver. The 
function is called in global scope and we don't have the object "before dot" anymore.

We can came across a similar problem when using `setTimeout` function. `setTimeout` function is a method of the global object and
it will call our function with `this` set to the global object. The first possible solution for this is wrapping the function
in a function that will set `this` to the object that called the method. After this change `coffee.orderCoffee` will be invoked
as a method.

```javascript
const coffee = {
    name: "Cappuccino",
    price: 2.5,
    orderCoffee() {
        console.log(`Can I get ${this.name}, please?`);
    }
}

setTimeout(function() {
    coffee.orderCoffee(); // Can I get Cappuccino, please? (this === coffee)
}, 1000);
```

## Using .bind(), .call() and .apply(), 
Functions provide a built-in method `bind` that allows to create a new function with a preset context. Here's the basic syntax:

```javascript
const bounded = func.bind(context);
```
`bounded` is a special function-like object, that is callable as function and passes the call to `func` setting `this = context`.
In other words `bounded` is a function that will be called with `this` set to `context`.

Now let’s try with an object method:

```javascript
const coffee = {
    name: "Cappuccino",
    price: 2.5,
    orderCoffee() {
        console.log(`Can I get ${this.name}, please?`);
    }
}

setTimeout(coffee.orderCoffee.bind(coffee), 1000); // Can I get Cappuccino, please? (this === coffee)

const order = coffee.orderCoffee.bind(coffee);
// can be run withoud an object
order() // Can I get Cappuccino, please? (this === coffee)
```
`bind` will create a new `orderCoffee` function that will be called with `this` permanently set to `coffee`.

Now you can ask - what about .call() and .apply()? 

The `call()` method calls a function with a given `this` value and arguments provided individually. The main differces between `call` and `bind` is that `call`
method: 
- excecutes the function it was called on right away
- accepts additional parameters
- the `call` method does not make a copy of the function but uses the original one.

`call` and `apply` serve the same purpose. The only difference is that `apply` accepts an array of arguments, whereas
`call` accepts a comma-separated list of arguments.

```javascript
function getOrder(coffee, size) {
    console.log(`Customer: ${this.name} ordered: ${size} ${coffee}`);
}

const coffee = {
    name: "Giovanni Giorgio",
}

getOrder.call(coffee, "cappuccino", "large"); // Customer: Giovanni Giorgio ordered: large cappuccino
getOrder.apply(coffee, ["cappuccino", "large"]); // Customer: Giovanni Giorgio ordered: large cappuccino
```

## Arrow functions
Imagine that you have to forget about everything that you've learned so far and start from scratch. It is not fully true, 
but behavior of `this` in arrow functions is slightly different comparing to function declarations or function expressions.

An arrow function does not have its own `this` binding. It is bound to the `this` of the enclosing execution context.

```javascript
const coffee = {
    name: "Cappuccino",
    price: 2.5,
    orderCoffee: () => {
        console.log(`Can I get ${this.name}, please?`);
    }
}

coffee.orderCoffee(); // Can I get undefined, please? (this === global)
```

Assigning the property `name` to the global object would solve this issue.

```javascript
global.name = "Espresso"

const coffee = {
    name: "Cappuccino",
    price: 2.5,
    orderCoffee: () => {
        console.log(`Can I get ${this.name}, please?`);
    }
}

coffee.orderCoffee(); // Can I get Espresso, please? (this === global)
```

As you can see to understand the value of `this` in arrow functions we have to look to the first non-arrow function 
scope. That is why arrow function from the example above will not display "Cappuccino" - it's because `coffee` is an object
and not a function scope.

This knowledge could be extremely useful when you want to access `this` within a callback. Let's leave the coffee related
examples for a while and take a look at the code below that uses `setInterval`:

```javascript
const counter = {
    count: 0,
    incrementCouter() {
        setInterval(function() {
            console.log(++this.count)
        }, 1000)
    }
}

counter.incrementCouter(); // NaN, NaN, NaN and so on...
```

It does not work. Why? `this` binding within our function expression refers to the global object because that's how `setInterval` works.
Since there is no `count` property in the global object, it will not be able to access it. We're getting `NaN` as a result 
because incrementing `++undefined` returns `NaN`.

Here is the solution:
```javascript
const counter = {
    count: 0,
    incrementCouter() {
        setInterval(() => {
            console.log(++this.count)
        }, 1000)
    }
}

counter.incrementCouter(); // 1, 2, 3...
```

Important note. `this` binding in arrow functions cannot be set explicitly. `this` argument passed to an arrow function
using `call()`, `apply()` or `bind()` is ignored. In other words, it doesn't matter how we call an arrow function.
`this` argument will always refer to the `this` value that was captured when the arrow function was created.

## Summary 

For the sake of simplicity, there is a list of questions that you should answer while evaluating the value of `this`. 
The order of the questions is really important.

- is the function called with new?
Then `this` refers to a constructor object.

- is the function called with call, apply or bind?
Then `this` refers to the context passed as the argument call, apply or bind methods

- is the function called as a method, ie: obj.method()?
Then `this` refers to the object "before the dot" in the method call

- is the function called in the global scope?
Then `this` refers to the global object, which is `window` in browsers.