JavaScript's "This" Keyword Simple Intro
Tags: javascript, beginners, webdev
Published: 2022-10-20 01:27:03


For JavaScript, `this` as part of a function is a tricky keyword because it behaves differently due to the implicit context of the function being called.  As part of an object, a short answer for describing `this` is that its a keyword in a function that uses the object that the function is contained in as its value.  With objects containing functions as properties, these functions are methods of the object and when executed use the object as its execution context.  As an example, say we have an object called `obje` and one of the keys of `obje` is `num` with a value of "123" while the second key is `str` with 'abc' and the third is a function called `func` whose value is a function that contains `console.log(this)`.  

```javascript
const obje = {
num: 123,
str: 'abc',
func: function(){
console.log(this)
}}
```
When you call `obje.func` with `()` , what results is the console logs the contents of `obje` in its entirety.  

```javascript
obje.func()
//{ num: 123, str:'abc', func: [Function: func] }
```

With the example provided, an easy way to describe `this` is that its value is typically the containing object to the left of the method being called.  In the global context when a function containing `this` is called, the value of `this` will be the global object which in browsers is also usually called the `window`.  

```javascript
console.log(this)
Window {0: Window, window: Window, self: Window, document: document, name: '', location: Location, …}
```
![Image description: this in chrome console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r6jlr12g351o78fguevj.png)

The `window` contains all the things like functions, variables, and objects that the user has access to both visible such as declared by the user and provided from behind the scenes such as built in JavaScript methods.  To prevent `this` from using the global object as its value, we can use `use strict` to change its value to undefined in a function.
![Image description: this in chrome console with use strict](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lcbaooz3g0gqrvm41p0s.png)
```javascript
function whatsThisHere(){
  console.log(this)
};
whatsThisHere()
//<ref *1> Object [global] {
//global: [Circular *1],...
```
```javascript
function whatsThisHere(){
  'use strict'
  console.log(this)
};
whatsThisHere()
//undefined
//undefined
```

There are three methods commonly used for functions utilizing `this` which are `call` , `apply` and `bind` . These methods are useful because ordinarily when functions using `this` are called in the global context or a context that doesn't use the containing object, the value of `this` no longer points to the object the function was originally in and usually results in an error when the function is executed.  

```javascript
const obje = {
num: 123,
str: 'abc',
func: function(key, key2){
return `Here is ${this[key]} and ${this[key2]}`};
}}
obje.func('num', 'str')
//'Here is 123 and abc'
const globFunc = obje.func
globFunc('num', 'str')
//'Here is undefined and undefined'
```

```javascript
const obje = {
num: 123,
str: 'abc',
nestFunc: {
  innerFunc: function(key, key2){
return `Here is ${this[key]} and ${this[key2]}`}
  }
};
obje.nestFunc.innerFunc('num','str')
//'Here is undefined and undefined'
```
By using the three methods, we can explicitly set the value of `this` but they can also be used to help abstract functions that are already not part of an object.

```javascript
function example(){return this}
example.call(obje)
//or
example.apply(obje)
//{ num: 123, str: 'abc', func: [Function: func] }
```
`call` can be used to execute a function containing `this` with the argument parameters allowing the object value of `this` to be set as the first argument in `call` and the rest of the function arguments to be passed after the first argument.  

```javascript
const obje = {
num: 123,
str: 'abc',
};
const globFunc = function(key, key2){
return `Here is ${this[key]} and ${this[key2]}`};

const globNum = 'num';
const globStr = 'str';
globFunc.call(obje, globNum, globStr)
//or
globFunc.call(obje, 'num', 'str')
//'Here is 123 and abc'
```
`apply` works like `call` with the main difference being that the arguments for the function parameters after setting `this` is provided as an array instead of separate arguments being written individually.  

```javascript
const exArray = ['num','str']
globFunc.apply(obje, exArray)
//or 
globFunc.apply(obje, ['num','str'])
//'Here is 123 and abc'
```
`call` and `apply` are similar in that they execute the the function immediately with a set parameter for `this` and additional arguments are passed as part of the function.  The `bind` method can be used to set the context object for `this` where the object is passed as an argument, but behaves differently.   

`bind` is used for assigning `this` a value though a function expression declaration to create a separate function with a set object 'bound' to the function.  There's no need to add additional arguments for using `bind` other than the object unless you want to have an argument permanently attached to the new function with the object.  The strength of `bind` allows us to create a copy of the function with `this` bound to the `object` value chosen, while adding additional arguments as with `call` and `apply` will become the default argument and it can't be changed later.  Unlike `call` and `apply`, `bind` isn't executed immediately. 

```javascript
const obje = {
num: 123,
str: 'abc',
func: function(key){
return `Here is ${this[key]}`};
}}
const bindFunc = obje.func.bind(obje);
bindFunc('num')
//'Here is 123'
bindFunc('str')
//'Here is abc'

const bindFuncWithArg = obje.func.bind(obje, 'num')
bindFuncWithArg('str')
//'Here is 123'
bindFuncWithArg()
//'Here is 123'

```

In our assignments in Flatiron, we learned about the "lost context bug", which occurs when `this` inside a function loses its context and it can be due to being a nested function that does not share the same context as the object being referenced.  

```javascript
const obje = {
  fruitColors: {
    apple: "red",
    banana: 'yellow',
    orange: "orange",
  },
  fruits: ['apple','banana','orange'],
};

function whatColorFruit(fruit){
  console.log(`The ${fruit} is ${this.fruitColors[fruit]}`)
};
whatColorFruit.call(obje, 'apple')
//The apple is red

function whatColorAll(){
  const whatColor = function(fruit){
    console.log(`The ${fruit} is ${this.fruitColors[fruit]}`)};
  this.fruits.forEach(whatColor);
};
whatColorAll.call(obje)
//TypeError: Cannot read properties of undefined (reading 'apple')
```
There are different ways to deal with the lost context situation, one technique to remedy this bug is to use `bind` to explicitly set the context of `this` on the nested function to be used for the `forEach` method.  

```javascript
function whatColorAll(){
  const whatColor = function(fruit){
  console.log(`The ${fruit} is ${this.fruitColors[fruit]}`)
}.bind(this);
  this.fruits.forEach(whatColor);
};
whatColorAll.call(obje)
//The apple is red
//The banana is yellow
//The orange is orange
```
or
```javascript
function whatColor(fruit){
    console.log(`The ${fruit} is ${this.fruitColors[fruit]}`)};
function whatColorAll(){
  const bindColor = whatColor.bind(this)
  this.fruits.forEach(bindColor);
};
whatColorAll.call(obje)
//The apple is red
//The banana is yellow
//The orange is orange
```
Here we use `call` to set the value of `this` to `obje`, and `bind` creates a new function with the value of `this` set to `obje`(taken from `call`) so that the value of `this` doesn't get lost when put into the `forEach` method.

Another way is to manually set the context by saving the value of `this` to a variable in the outer scope and use that variable in the nested function instead of `this` so we can point to the object being used without losing the value of `this` in the nested function context.  

```javascript
function whatColorAll(){
  const self = this;
  const whatColor = function(fruit){
    console.log(`The ${fruit} is ${self.fruitColors[fruit]}`)};
  this.fruits.forEach(whatColor);
};
whatColorAll.call(obje)
//The apple is red
//The banana is yellow
//The orange is orange
```
A third simple solution is to use an arrow function in the nested function instead of a function expression since arrow functions use the context of the parent function instead of creating another context.  This means we don't have to manually set the value of `this` like with the other two options.   
```javascript
function whatColorAll(){
  const whatColor = fruit => {
    console.log(`The ${fruit} is ${this.fruitColors[fruit]}`)};
  this.fruits.forEach(whatColor);
};
whatColorAll.call(obje)
//The apple is red
//The banana is yellow
//The orange is orange
```

## Conclusion

The keyword `this` is a little strange in the way it behaves, especially since it has the potential to be something different based on the context of how the function is called.  One way to keep track of its value is simply to console.log the value of `this` to see what object is being used.  

```javascript
function whatsThisEach(){
  const whatsThis = function(fruit){
    console.log(this)
    };
  this.fruits.forEach(whatsThis);
};
whatsThisEach.call(obje)
//<ref *1> Object [global] {
//global: [Circular *1],
```
Here we see that the value of `this` is the global object, which helps explain why it didn't work earlier if we didn't set the value of `this` or use the arrow function.

```javascript
function whatsThisEach(){
  const whatsThis = fruit => {
    console.log(this)
    };
  this.fruits.forEach(whatsThis);
};
 whatsThisEach.call(obje)
//{fruitColors: { apple: 'red', banana: 'yellow', orange: 'orange'}, fruits: [ 'apple', 'banana', 'orange' ]}
//{fruitColors: { apple: 'red', banana: 'yellow', orange: 'orange' }, fruits: [ 'apple', 'banana', 'orange' ]}
//{fruitColors: { apple: 'red', banana: 'yellow', orange: 'orange' }, fruits: [ 'apple', 'banana', 'orange' ]}
```

Having the knowledge of how it behaves and how we can use `call` `apply` or `bind` to help set the value of `this` allows us to utilize a tool that helps abstract functions by make them more applicable for working with different objects across different execution contexts.

Resources
- this - JavaScript | MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this)
- Function.prototype.call() - JavaScript | MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call)
- Function.prototype.apply() - JavaScript | MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)
- Function.prototype.bind() - JavaScript | MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)
- Flatiron School (https://flatironschool.com/)