# "this" in JavaScript

We will try to understand how `this` works, with some concepts and examples as well.

`this` has different values depending on where it is used: 
So will understand this inside the following...

1. Global Scope
2. Objects
3. Methods
4. Functions (including strict mode)
5. Inner Functions
6. Constructor Functions
7. Classes

# Introduction to `this`
If you are inside a specific scope or object or function, this really means whichever object you are in.

> There are some exceptions which we will look at

# 1. `this` inside Global Space
Variables declared Globally (outside any function) have Global Scope.

```
console.log(this); // window object
```
```
this.table = 'Window Table';
console.log(window.table); // Window Table
```
Here in the above example `table` is a property of `window` object, so when we print window.table it prints the value as Window Table.


Another example - 
```
this.garage = {
    table: "Garage Table"
}
```

To invoke this above code, we can write it as
```
this.garage.table;
// Garage Table

window.garage.table;
// Garage Table
```

Here `this` is public property of `window` object because when you say `this`, you are creating a public property that can be accessible from outside.

# 2. `this` inside Object
> `this` inside Object is a private variable or private scoped.

### Example
```
let SoubhagyaRoom = {
    table: "Soubhagya Table"
}
```
Here to access My Table, we can't use `this.SoubhagyaRoom.table`. It will give a TypeError saying Cannot read property 'table' of undefined. As it's not accessible.

As its a private variable we have to use it as `SoubhagyaRoom.table;`

# 3. `this` inside a method

> Objects can have methods as well.

> In an object method, `this` refers to the "owner" of the method.

### Example
```
let SoubhagyaRoom = {
    table: "Soubhagya's Table",
    cleanTable() {
         console.log(`Cleaning ${this.table}`)
    }
}

SoubhagyaRoom.cleanTable(); // Cleaning Soubhagya's Table
```

# 4. `this` inside a Function

### Example
```
this.table = 'Window Table';
const cleanTable = function() {
    console.log(`Cleaning ${this.table}`);
}

cleanTable();
```
> In the above example it will access the global scope and print `Cleaning Window Table`

> But there is an exception to `this` while using the strict mode.
The same function in strict mode will result in `TypeError: Cannot read property 'table' of undefined`

### Using `'use strict'`
```
'use strict'
this.table = 'Window Table';
const cleanTable = function() {
    console.log(`Cleaning ${this.table}`);
}

cleanTable();
```

### Fix for `this` in `'use strict'`

> To resolve this issue we have `call()` to the rescue

```
'use strict'
this.table = 'Window Table';
const cleanTable = function() {
    console.log(`Cleaning ${this.table}`);
}

cleanTable.call(this);
```

> It says, execute the cleanTable function as if its a method of this and `this` being in this case - `Global Object`

> call() is basically used to call somebody else's method on somebody else's object.

### Example
```
this.table = 'Window Table';
const cleanTable = function(soap) {
    console.log(`Cleaning ${this.table} using ${soap}`);
}

this.garage = {
    table: "Garage Table"
}

let SoubhagyaRoom = {
    table: "Soubhagya's Table"
}

cleanTable.call(this, 'some soap');
// Cleaning Window Table using some soap
cleanTable.call(this.garage, 'some soap');
// Cleaning Garage Table using some soap
cleanTable.call(SoubhagyaRoom, 'some soap');
// Cleaning Soubhagya's Table using some soap
```
# 5. `this` inside an Inner Function
### Using `'use strict'`
### Example
```
'use strict'
this.table = 'Window Table';
const cleanTable = function(soap) {
  const innerFunction = function(_soap) {
    console.log(`Cleaning ${this.table} using ${_soap}`);
  };
  innerFunction(soap);
};

this.garage = {
    table: "Garage Table"
}

let SoubhagyaRoom = {
    table: "Soubhagya's Table"
}

cleanTable.call(this, 'some soap');
cleanTable.call(this.garage, 'some soap');
cleanTable.call(SoubhagyaRoom, 'some soap');
```
> If we execute the above function then we will get `TypeError: Cannot read property 'table' of undefined`

This is because inside a function using `this` is not very useful unless you are using `call()` or `bind()`.

So to use `this` inside the `innerFunction`, we need to let it know it's a method of some sort or it would execute on that particular object.

To fix the issue, we have 4 solutions. Let's take a look,

### Solution 1 - Define `this` outside of innerFunction
```
'use strict'
this.table = 'Window Table';
const cleanTable = function(soap) {
  let that = this;
  const innerFunction = function(_soap) {
    console.log(`Cleaning ${that.table} using ${_soap}`);
  };
  innerFunction(soap);
};

this.garage = {
    table: "Garage Table"
}

let SoubhagyaRoom = {
    table: "Soubhagya's Table"
}

cleanTable.call(this, 'some soap');
cleanTable.call(this.garage, 'some soap');
cleanTable.call(SoubhagyaRoom, 'some soap');
```

### Solution 2 - Using `call()` with innerFunction
```
'use strict'
this.table = 'Window Table';
const cleanTable = function(soap) {
  const innerFunction = function(_soap) {
    console.log(`Cleaning ${this.table} using ${_soap}`);
  };
  innerFunction.call(this,soap);
};

this.garage = {
    table: "Garage Table"
}

let SoubhagyaRoom = {
    table: "Soubhagya's Table"
}

cleanTable.call(this, 'some soap');
cleanTable.call(this.garage, 'some soap');
cleanTable.call(SoubhagyaRoom, 'some soap');
```
### Solution 3 - Using `bind()` with innerFunction
```
'use strict'
this.table = 'Window Table';
const cleanTable = function(soap) {
  const innerFunction = function(_soap) {
    console.log(`Cleaning ${this.table} using ${_soap}`);
  };
  innerFunction.bind(this)(soap);
};

this.garage = {
    table: "Garage Table"
}

let SoubhagyaRoom = {
    table: "Soubhagya's Table"
}

cleanTable.call(this, 'some soap');
cleanTable.call(this.garage, 'some soap');
cleanTable.call(SoubhagyaRoom, 'some soap');
```

### Solution 4 - Using `arrow function`
```
'use strict'
this.table = 'Window Table';
const cleanTable = function(soap) {
  const innerFunction = (_soap) => {
    console.log(`Cleaning ${this.table} using ${_soap}`);
  };
  innerFunction(soap);
};

this.garage = {
    table: "Garage Table"
}

let SoubhagyaRoom = {
    table: "Soubhagya's Table"
}

cleanTable.call(this, 'some soap');
cleanTable.call(this.garage, 'some soap');
cleanTable.call(SoubhagyaRoom, 'some soap');
```
> Arrow functions are special because if you want to use `this` inside a function, it doesn't really know what to do with it.

> But if I have an arrow function and if you use `this` inside, it will basically take `this` from the outer scope and use that.

# 6. `this` inside a Constructor
### Example
```
let createRoom = function(name) {
  this.table = `${name}'s table`;
}

createRoom.prototype.cleanTable = function(soap) {
  console.log(this)
  console.log(`Cleaning ${this.table} using ${soap}`);
}

const smRoom = new createRoom('Soubhagya');
smRoom.cleanTable('some soap');

const rmRoom = new createRoom('Raghav');
rmRoom.cleanTable('some soap');
```

Instead of using the constructor function here, we can use classes.

# 7. `this` inside a class
### Example

```
class createRoom {
  constructor(name) {
    this.table = `${name}'s table`
  }

  cleanTable(soap) {
    console.log(`Cleaning ${this.table} using ${soap}`)
  }
}

const smRoom = new createRoom('Soubhagya');
smRoom.cleanTable('some soap');

const rmRoom = new createRoom('Raghav');
rmRoom.cleanTable('some soap');
```

When we have a class, it pretty much does the same thing that a constructor function does and it has a prototype method called cleanTable.
