Understanding ‘this’ in JavaScript and determining it’s value in 4 simple steps

Igor Łuczko
6 min readJul 31, 2020

Every frontend developer at some point of his career has stumbled into thisin JavaScript and my bet is the first experience wasn’t very clear.

It turns out that in fact this in JavaScript can be understood and that we can even follow a simple 4 step procedure to determine it’s value.
Let’s solve this riddle.

Introduction

If you are looking into thisit probably means one of the three things:

  • you stumbled upon some bug, lost hours and have thought to yourself that it’s finally time to understand this. Or maybe you have to support some legacy project
  • you are preparing for a job interview or some sort of an exam (yes, thiswill be asked :) )
  • you are just curious about JavaScript

As a disclaimer, I know a lot of developers who (without fully understanding this) still manage to write production quality code.

For instance, if you work in React for not a long time, chances are that you operate on functional components and hooks only and haven’t seen this for a long time.

I personally believe that it’s worth to have an understanding of the basics as this is really not that difficult and at some point of your career it will definitely pay off.

TL;DR

To determine value of this we just have to follow simple 4 step procedure. It comes down to inspecting where and how it’s called.

The first condition that we meet determines this value.

  1. Did we use new keyword i.e. new MyFunction()?
  2. Did we use call, bind, apply i.e. myObject.myFunction.call(myObject2) ?
  3. Did we use the dot notation i.e. myObject.myFunction()?
  4. Did we use free function invocation i.e. window.myFunction() or just myFunction() ?

Above does not apply for arrow functions.
For arrow functions we have to inspect thisvalue of the surrounding context.

Have a read below for more detailed explanation.

Rules to follow

There are four rules that we need to inspect in order to determine this.
Disclaimer — none of these values apply to arrow functions — more on that later on.

1. The newkeyword.

JavaScript has the keyword new.
If we use it when we call the function, this inside the function is new object.

function Example() {
console.log(this); // {}
this.value = 'puma';
console.log(this); // { value: 'puma' }
}
new Example()

As an important side note — if we are to call the Example() without the new keyword, in the browser enviroment we would create ourselves a Window.valuevariable, which can easily lead to bugs (look into point 4 for more details).

Example();// function invoked without the new keyword refers this to Window
// Window { parent: Window, opener: null, top: Window ... }

If we are in strict (‘use-strict’) mode (on production level we must be), things change a bit:

Example(); // undefinedwindow.Example() 
// Window { parent: Window, opener: null, top: Window ... }

2. The trio call, bind, apply.
If either of them is used, this inside the function is the object in the argument.

function exampleFunction() {  console.log(this);}const obj = {  value: 'puma'};const boundedFunction = exampleFunction.bind(obj);boundedFunction();     // { value: 'puma' }exampleFunction.call(obj);  // { value: 'puma' }exampleFunction.apply(obj); // { value: 'puma' }exampleFunction(obj) 
// Window
// undefined in strict mode

3. Calling function as object or class method.

This is actually quite intuitive.

Whenever we have an object that has a method and we use a dot notation to invoke the function, this is the object to the left of the dot i.e.

const myObject = {
value: 'puma',
myFunction: function() {
console.log(this);
}
};
myObject.myFunction(); // this is the object to left of the dot// -> { value: 'puma', myFunction: ƒ }

4. Free function invocation

This is very similar to point 3.
If we create a function not as a method, it automatically becomes a property of global object, window.

function myFunction() {
console.log(this);
}
myFunction();window.myFunction();// if not in strict mode
// this is doing exactly the same thing
// output if called in browser
// Window {stop: ƒ, open: ƒ, alert: ƒ, ...}

In this scenario, this is a global object — window.
Remember how in point 3 we said myObject.myFunction?
We have said that this is an object that is on the left hand side of the dot i.e. myObject. Same rule applies when we say window.myFunction.
Should we say myFunction what we implicitly say is window.myFunction.

console.log(myFunction === window.myFunction); // true

This looks slightly different in the strict mode:

'use strict';function myFunction() {
console.log(this);
}
myFunction(); // undefined
window.myFunction(); // Window { ... }

5. If multiple rules apply, the one on the top takes precedence.
For instance, rule 2 (call) takes precedence over rule 3 (the dot notation).

new keyword 
call, bind, apply
myObject.myFunction
window.myFunction

Arrow functions

Arrow functions are a special case. One of the key reasons they were created, was to simplify this.

Rules introduced earlier are ignored, and this value receives the value of the surrounding scope.

In simple terms, we can go level above the arrow function and inspect this value in that level. The arrow function has the same this value i.e.

const myObject = {  value: "puma",  createArrowFunction: function () {    return () => console.log(this);  },};const arrowFunction = myObject.createArrowFunction();arrowFunction();// { value: "puma", createArrowFunction: ƒ }

We often meet with arrow functions in React be it class or functional components.

As a side note, you cannot use new with arrow functions — they cannot be used as a constructor.

const myArrowFunction = () => console.log(this)new myArrowFunction();// Uncaught TypeError: myArrowFunction is not a constructor

Exercises

Let’s put the theory into practice and try to determine this value.

First exercise

const myObject = {  value: "puma",  myFunction: function () {    console.log(this);  },};const printThis = myObject.myFunction;myObject.myFunction(); // { value: "puma", printThis: ƒ }printThis(); // in non strict mode: Window {stop: ƒ, open: ƒ, alert: ƒ, ...}

ok, so what is going on here?

myObject.myFunction()

To understand the result let’s go through the rules we have learned at the beginning of the article:

First rule — have we used new keyword? No, moving on.
Second rule — have we used call, bind, apply? No, moving on.
Third rule — have we used dot notation? Yes, we have. This is why this has the value of object from left hand side of the dot.

printThis()

This is in fact a common interview question.
Let’s inspect the rules one more time:

First rule — new keyword? No, moving on.
Second rule — call, bind, apply? No, moving on.
Third rule — dot notation? No, moving on.
Fourth rule — free function invocation — yes.
This is the rule that we have met. In strict mode, we would get undefined value, in not strict mode we will get globalWindow object.

Second exercise

To start with, I haven’t seen code like that for a long time. Sometimes we do however have to support legacy projects. Or pass a job interview and questions related to this are still very common one.

const myObject = {  value: "puma",  myFunction: function () {    console.log(this);  },};const myObject2 = { value: "Will I overwrite myObject.value?" };myObject.myFunction.call(myObject2);// { value: "Will I overwrite myObject.value?" }

In order to understand what is going on here, let’s go through the rules once again:

  1. Have we used new? No, moving on.
  2. Have we used call, bind, apply ? Yes, we have. This rule tells us that this value will take the myObject2. This is our answer but for the sake of understanding we will go one level down.
  3. Have we used dot notation? Yes, we have. But Rule 2 is more important hence take precedence.

A word of caution about libraries

This article applies to Vanilla JavaScript concepts. Some libraries change behaviour of this.
For instance, jQuery binds this to the DOM element triggering an event in the callback to that event i.e.

<button class = "click-me"> jQuery bind this to me </button>$('.click-me').on('click', function() {});

If a library changes this behaviour, always verify it’s documentation. Most likely it’s using bind to bound.

Summary

As we can see, determining this value is not that difficult if we follow a simple procedure.

The first condition that we meet determines this value.

  1. Did we use new keyword i.e. new MyFunction()?
  2. Did we use call, bind, apply i.e. myObject.myFunction.call(myObject2) ?
  3. Did we use the dot notation i.e. myObject.myFunction()?
  4. Did we use free function invocation i.e. window.myFunction() or just myFunction() ?

Above does not apply for arrow functions.
In that case, we have to inspect thisvalue of the surrounding context.

Happy debugging and enjoy the interview!

--

--

Igor Łuczko

Technical Lead. Seeking to make a change. Doing quality work that matters for people who care.