CodeNewbie Community 🌱

Cover image for JavaScript: Understand 'this' keyword
Mariana Simon
Mariana Simon

Posted on • Originally published at learnpine.com

JavaScript: Understand 'this' keyword

'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:
 

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
 

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

 

Context explained

 

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:

 

var name ='Beyonce'  

console.log(this.name)  // Beyonce 
console.log(window.name)  // Beyonce 
Enter fullscreen mode Exit fullscreen mode

 

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'.

 

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. 
Enter fullscreen mode Exit fullscreen mode

 

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.

 

'this' with bind

 
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:
 

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. 
Enter fullscreen mode Exit fullscreen mode

 

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.

 

'this' with call

 
Like bind, call can be used to set a custom value to 'this'.

See the example:
 

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. 
Enter fullscreen mode Exit fullscreen mode

 

'this' with arrow functions

 
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?
 

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. 
Enter fullscreen mode Exit fullscreen mode

 

Even if you use call/bind, it won't work. It still logs "Ash". Why?

 

'this' with regular vs arrow functions

 

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!

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. 
Enter fullscreen mode Exit fullscreen mode

 
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.

 

Useful cases of 'this' with arrow functions

 
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].
 

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(); 
Enter fullscreen mode Exit fullscreen mode

 

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.
 

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(); 
Enter fullscreen mode Exit fullscreen mode

 

About me, let's connect! 👋👩‍💻

 
Thanks for reading! I'm an avid learner and I love sharing what I know. I teach coding live for free 👉 here and I share coding tips on my Twitter . If you want to, come visit and say hi in the chat 😁
 

 

Top comments (4)

Collapse
 
recursivedan profile image
Dan

Thanks! 😊

Collapse
 
andrewbaisden profile image
Andrew Baisden

Good explanation a lot of people struggle with it.

Collapse
 
aritdeveloper profile image
Arit Amana

Amazing explanation! Thank you for sharing ❤️

Collapse
 
simonpaix profile image
Mariana Simon

Thanks, Arit! Glad you liked it :)