JavaScript: Understand 'this' keyword
Tags: webdev, javascript, beginners, programming
Published: 2021-05-26 19:16:38

`'this'` is one of the most important concepts in JavaScript. It's part of the basics and the sooner you get comfortable with it the easier your coding life will be :)   

Make sure you understand the use of 'this' by reading this post.  

First, the simple 5 yo explanation: 
&nbsp;

> The use of `'this'` in programming is like what you do in plain English. For example, when you say "I got a job! This is great!". We know 'this' is referring to the fact you got the job. In other words, 'this' gives context to the second sentence. 

--5 yo 
&nbsp;

So, to understand `'this'`, you need to know what context is.  

&nbsp;  
## Context explained 
&nbsp; 

Context is related to objects. It refers to the object a method or property belongs to. Your code starts running in a global context, which in your browser is the `window` (in Node, the global object is called `global`). Look at the example below: 

&nbsp; 
```javascript 
var name ='Beyonce'  

console.log(this.name)  // Beyonce 
console.log(window.name)  // Beyonce 
```
&nbsp;

In the example, `'this'` equals `window` because I ran it on my browser, where the global object is the window.  So, window.name ==="Ash"  .  So far, the context is window. Ok. 

  
Now, the context changes during your code execution. Whenever an object's method is called, `'this'` is set to the object the method was called on. 

  
See the example below. Both lines 4 and 10 are identical but they log different results according to the value of `'this'`. 

&nbsp;  
```javascript 
var name = 'Beyonce' 

function sayMyName(){ 
  console.log(`Your name is ${this.name}.`) //  'this' is  window 
}  

var heisenberg = { 
  name: 'Heisenberg', 
  sayMyName: function () { 
    console.log(`Your name is ${this.name}.`) //  'this' is heisenberg 
  }  
}  

sayMyName()   // Your name is Beyonce. 
heisenberg.sayMyName() // Your name is Heisenberg. 
```
&nbsp;

The code above works fine, but we repeated line 4 which is not cool (remember: DRY Don't Repeat Yourself). 

There is a way to write the `console.log()` only once and reuse it. To do that, we use the function `bind`. 

&nbsp;
## 'this' with bind 
&nbsp;
`Bind` applies a given `'this'` (an object) to the function that is calling it. The object that you want to bind to the function is passed as a parameter to bind. 

See example: 
&nbsp;
```javascript 
function sayMyName(){ 
  console.log(`Your name is ${this.name}.`)  
}  

var beyonce = { 
  name: 'Beyonce', 
} 

var heisenberg = { 
  name: 'Heisenberg', 
} 

let sayBeyonce= sayMyName.bind(beyonce)    
let sayHeisenberg= sayMyName.bind(heisenberg) 

sayBeyonce() // Your name is Beyonce. 
sayHeisenberg() // Your name is Heisenberg. 
``` 
&nbsp;

Nice! Now, let's say we didn't want to create new functions to say each person's name.  We only want to use sayMyName() .   

We can do so with the `call` function and a generic person object. 
 
&nbsp;
## 'this' with call 
&nbsp;
Like bind, `call` can be used to set a custom value to `'this'`. 

See the example: 
&nbsp;
```javascript 
var person = { 
  sayMyName: function(){ console.log(`Your name is ${this.name}.`)}; 
} 

var beyonce = { 
  name: 'Beyonce', 

};   

var heisenberg = { 
  name: 'Heisenberg', 
}; 
  
person.sayMyName.call(beyonce); // Your name is Beyonce. 
person.sayMyName.call(heisenberg); // Your name is Heisenberg. 
``` 

&nbsp;
## 'this' with arrow functions 
&nbsp;
Be careful with arrow functions 🏹   

When an arrow function is used, it doesn’t set a new value to `'this'`. Instead, it inherits the one from the parent scope. 

This example is identical to the previous one, but with an arrow function instead of a normal one. 

It logs "Ash" twice to the console. Weird, right? 
&nbsp;
```javascript 
var name = 'Ash'; 

var person = { 
  sayMyName: () => console.log(`Your name is ${this.name}.`) 
}; 

var beyonce = { 
  name: 'Beyonce', 
};  

var heisenberg = { 
  name: 'Heisenberg', 
}; 

person.sayMyName.call(beyonce); // Your name is Ash. 
person.sayMyName.call(heisenberg); // Your name is Ash. 
```
&nbsp;
 
Even if you use call/bind, it won't work. It still logs "Ash". Why?  

&nbsp; 
## 'this' with regular vs arrow functions 
&nbsp; 

Regular functions set their own `'this'` object to the caller.
  
BUT, Arrow functions don't. They inherit `'this'` from the previous context, aka from the scope it sits inside. In this case, from window. This is called "lexical scoping". 

So, when we used an arrow function, `'this'` had nothing to do with the caller of the function. It was still equal to `window` and it stayed that way, logging "Ash" as a result. 

What if we wrap the arrow function with a regular function? 

The regular function is called and sets `'this'` to the caller person.  

The arrow function is called from inside the regular function. 'this' value in the arrow function inherits 'this' value from the outer (regular) function. **So it works!**   

```javascript 
var name = 'Ash'; 
  
var person = { 
  sayMyName: function () { 
    const arrowFunction = () => console.log(`Your name is ${this.name}.`); 
    arrowFunction(); 
  }, 
};  

var beyonce = { 
  name: 'Beyonce', 
}; 

var heisenberg = { 
  name: 'Heisenberg', 
};  

person.sayMyName.call(beyonce); // Your name is Beyonce. 
person.sayMyName.call(heisenberg); // Your name is Heisenberg. 
``` 
&nbsp;
Cool. So using arrow functions is bad? Not at all. There are many use cases when you actually want to inherit 'this' from the surrounding context. In those cases, arrow functions are super useful.  

&nbsp;
## Useful cases of 'this' with arrow functions 
&nbsp;
Let's see an example.  Here, we want to log a different name every 2 seconds by using an array of names. If we run this code, we get the error: **[Uncaught TypeError: Cannot read property 'forEach' of undefined].** 
&nbsp;
```javascript 
var people = { 
  names: ['Ash', 'Beyonce', 'Heisenberg'], 

  sayNames: function () { 

    // log each name after 1 second 
    setTimeout(function () { 
      console.log(this); 
      this.names.forEach(function (name) { 
        console.log('your name is' + name); 
      }); 
    }, 2000); 
  }, 
}; 

people.sayNames(); 
``` 
&nbsp;

Why? 

When sayNames is called it sets 'this' to be the people object. But when setTimeout is called, it sets 'this' to be the window. The window doesn't have a `names` property.  How do we solve that?  

You guessed it right! We use an arrow function, which will inherit the 'this' from its outer context. In other words, it will inherit `'this'` from sayNames context. 
&nbsp;
```javascript 
var people = { 
  names: ['Ash', 'Beyonce', 'Heisenberg'], 

  sayNames: function () { 
    console.log(this); 

    // log each name after 1 second 
    setTimeout( ()=> { 
      console.log(this); 
      this.names.forEach(function (name) { 
        console.log('your name is ' + name); 
      }); 
    }, 2000); 
  }, 
}; 

people.sayNames(); 
```

&nbsp;
## About me, let's connect! 👋👩‍💻 
&nbsp;
Thanks for reading! I'm an avid learner and I love sharing what I know. I teach coding live for free 👉 [here](https://learnpine.com "here") and I share coding tips on [my Twitter](https://twitter.com/simonpaix) . If you want to, come visit and say hi in the chat 😁 
&nbsp;
  
&nbsp;