loading...
Cover image for Take 'this' Quiz, Understand How 'this' Works in JavaScript

Take 'this' Quiz, Understand How 'this' Works in JavaScript

liaowow profile image Annie Liao Updated on ・4 min read

Among all the abstract ideas JavaScript has to offer, the 'this' keyword can be one of the most challenging concepts to grasp. On the surface, 'this' seems like an intuitive keyword that simply refers to the environment (or context) it resides in.

As you look deeper into the JavaScript runtime, i.e. where the code is being executed, 'this' keyword might end up grabbing things you did not expect.

In this post, I created 4 simple scenarios where 'this' keyword can be interpreted differently, each followed by a multiple-choice section, a long pause (in case you scrolled too fast and accidentally saw the answer), and the answer with explanations.

Feel free to play around with the code on your console or text editor. Remember, the more and varied situations you encounter, the better you'll be at identifying and understanding 'this' keywords.

Ready? Let's do this!

Challenge #1

const call = {
  caller: "mom", 
  says: function() {
    console.log(`Hey, ${this.caller} just called.`);
  }
};

call.says();

What will the code above log to the console?

(A) Hey, undefined just called.
(B) Hey, mom just called.
(C) Hey, caller just called.

...

...

...

...

...

...

...

...

...

...

The answer is...

(B) Hey, mom just called.

Here's the code block again:

const call = {
  caller: "mom", 
  says: function() {
    console.log(`Hey, ${this.caller} just called.`);
  }
};

call.says();

Here we have a function declaration inside the call object. As a general rule, 'this' is determined by the object invoking a function. Therefore, when the call object invokes says function (call.says()), the 'this' keyword inside the says function refers to the call object, making this.caller equal to "mom".

Pretty straight forward, right?

Challenge #2

const call = {
  caller: "mom", 
  says: () => {
    console.log(`Hey, ${this.caller} just called.`);
  }
};

call.says();

What will the code above log to the console?

(A) Hey, undefined just called.
(B) Hey, mom just called.
(C) Hey, caller just called.

...

...

...

...

...

...

...

...

...

...

The answer is...

(A) Hey, undefined just called.

Here's the code block again:

const call = {
  caller: "mom", 
  says: () => {
    console.log(`Hey, ${this.caller} just called.`);
  }
};

call.says();

Wait, isn't this code the same as the first one?

If you look closely, the function declaration from Challenge#1 is now replaced by an arrow function.

Arrow functions, as part of ES6 syntax, do NOT have their own 'this' keyword. Instead, they will use the 'this' keyword of whatever 'this' was outside the function when it was created.

In other words, 'this' inside the arrow function is not bound to our call object, but instead is already bound to where the call object is being created originally, which in this case is the global object. And because the global object does not know anything about say() function, 'this' is undefined. And because the global object does not have a caller property, this.caller is undefined. (shout out to James Nylen for the correction!)

Challenge #3

const call = {
  caller: "mom", 
  says: function() {
    console.log(`Hey, ${this.caller} just called.`);
  }
};

let newCall = call.says;

newCall();

What will the code above log to the console?

(A) Hey, undefined just called.
(B) Hey, mom just called.

...

...

...

...

...

...

...

...

...

...

The answer is...

(A) Hey, undefined just called.

What happened? Let's look at the code again:

const call = {
  caller: "mom", 
  says: function() {
    console.log(`Hey, ${this.caller} just called.`);
  }
};

let newCall = call.says;

newCall();

Here, we assign a new variable to the says function inside the call object. And then we invoke the variable, which is a simple function call.

Notice where we invoke the function. Is it inside the call object? No. We are invoking newCall() function globally, which in turn makes the 'this' keyword equal to the global object.

As demonstrated in Challenge#2, since the global object does not have a caller property, you get "undefined" as a result.

By now, you might notice a key pattern:
Regular functions change their behaviors BASED ON the object that is CALLING the function.

Challenge #4

function anotherCaller() {
  console.log(`${this.caller} called, too!`);
}

const call = {
  caller: "mom", 
  anotherCaller: anotherCaller,
  says: function() {
    console.log(`Hey, ${this.caller} just called.`);
  }
};

let newCall = call.anotherCaller;

newCall();

What will the code above log in the console?

(A) mom called, too!
(B) Hey, mom just called.
(C) undefined called, too!

...

...

...

...

...

...

...

...

...

...

The answer is...

(C) undefined called, too!

Again, pay attention to where the function is being invoked:

function anotherCaller() {
  console.log(`${this.caller} called, too!`);
}

const call = {
  caller: "mom", 
  anotherCaller: anotherCaller,
  says: function() {
    console.log(`Hey, ${this.caller} just called.`);
  }
};

let newCall = call.anotherCaller;

newCall();

We are invoking newCall() function globally, which means the 'this' keyword is referring to the global object. It doesn't matter that we are assigning newCall to a function inside the call object. We are calling newCall globally, and globally is where 'this' is assigned.

If you're feeling adventurous, try moving anotherCaller() function inside of the call object, like so:

const call = {
  caller: "mom", 
  anotherCaller: function() {
        console.log(`${this.caller} called, too!`)
      },
  says: function() {
    console.log(`Hey, ${this.caller} just called.`);
  }
};


let newCall = call.anotherCaller;
newCall();

Based on what we just discussed, what do you think the output will be?

Try running the code mentally before checking the answer in your browser. If you got it, you got this (the basics, at least)!


I hope these examples give you a better picture of how 'this' keyword works. If you still find it confusing, worry not. As with everything in programming, practice is key.

For more examples, check out the official MDN documentation on 'this'. I also highly recommend this awesome article. The author provided clear explanations and actually gave me additional insight into some tricky parts in my last challenge.

Posted on by:

liaowow profile

Annie Liao

@liaowow

Fresh software developer with full-stack skillset (React with Rails). Currently exploring data structures, d3 visualization and other front-end magic.

Discussion

pic
Editor guide
 

Nice! A quick, well-done test of this knowledge.

My goto whenever anyone asks about this is always You Don't Know JS (github.com/getify/You-Dont-Know-JS...).

 

As someone who learned about the self keyword (Smalltalk) and learning about this in JS, the article helps a lot! Especially that arrow function! So does this mean that it is better to create a Class for call instead of just a block variable? That way you can instantiate the Class call.

 

Glad it helps, Chi! And thanks for mentioning Smalltalk -- I had to look it up haha. I don't think there's a preferred way, it all depends on your use case. Also, JavaScript's class keyword is not really a true class as in other object-oriented languages like Smalltalk. I found this deep-dive article, hope it helps as well: scotch.io/tutorials/better-javascr...

 

Thanks for the fun quiz.
I was able to answer every question correctly thankfully to this post here on dev.


It helps to figure out this value for every possible case in 5 simple steps.
 

Loved the flow chart in that post. Thanks, Eugene!

 

I've been programming 15 years in 7+ languages now. Languages change constantly, never memorize anything. Javascript and other languages change as fast as you learn them. Right now I am creating an app in Scala 2 but I will have to rewrite and migrate it within 2 years to Scala 3. So much about the language is changing completely. I have not used PHP in less than 2 years it has changed a lot since. I am moving to physics at least it doesn't change every week LOL

 

Hi Annie, a couple of small corrections...

In the explanation for examples 2 and 3:

And because the global object does not know anything about say() function, 'this' is undefined.

Should be something like this instead...

And because the global object does not have a caller property, this.caller is undefined.

In the explanation for example 2:

(if you're using a browser console, you might see "null" (Firefox) or "[url referring to the window object]"(Chrome) instead of "undefined")

I tested in Chrome and Firefox and I get undefined in both browsers. This makes sense, because in these examples this.caller is equivalent to window.caller, which is undefined (unless some code on the current page has set this property).

 

Thank you, James! Yes. That makes much more sense. I was googling around trying to find the simplest explainer, only to complicate my own thought process. Really appreciate your input :)

 

I actually got 100% on this but I am constantly getting 'this' wrong.

Having it in this format really made me think about where the invocations and definitions were happening.

Really helpful exercise for a relative novice like me - thanks Annie!

 

Just gotta keep practicing. Thanks for following along, Sam! Made my day.

 

It's good to practice 'this' like this but there's always a whole bunch of other scenarios that might still trip people up. The best thing to do is to have some theory to solidify the understanding:

  1. 'this' is an object created for every Execution Context that is created (on program startup and on every function invocation).
  2. 'this' can change based on how/where the function is called/invoked.
  3. There are four patterns of invoking functions that define the context of the function being called:
  • function invocation (e.g. says())
  • method invocation (e.g. call.says())
  • constructor invocation pattern (when you call a constructor function with the 'new' keyword - e.g. var person = new Caller())
  • apply invocation pattern (when you provide your own context while calling the function with say.call() or say.apply(), or if you explicitly bind the context with say.bind()) Here's a really good article about these four patterns (read until the React part): reactkungfu.com/2015/07/why-and-ho...
 

Couldn't agree more. Thanks so much for providing an overarching concept around 'this'. That could've been a better opening for 'this' post (pun intended).

 

Nice !! You should add other tricky ones with something like :

newCall.call(call);

and :

newCall.bind(call)();
 

Thanks, Django! I actually thought about that, but then I'd have to introduce call, apply and bind, which might turn this little quiz into a full-fledged code challenge lol.

 

Just to be nitpicking the correct solution for challenge #4 is not "Undefined called, too!" but "undefined called, too!" (lowercase).
Besides, thanks for the refresh.
The hardest part for me was to remember that an object doesn't have to be new'd to create it's context.

 

Haha, good call. Thanks, Peter! That's a good reminder for me too, especially when working on more complex projects.

 

Hey, there are nice tasks=)
If you have a plan to prepare next episode of this keyword challenges then have a look at my post about this in setTimeout callback:
dev.to/smlka/easyspec-how-does-set...

 

Nice quiz! Just had challenge # 2 wrong...helped me realize I didn't have a full understanding of arrow functions

 

Thanks Maria! Yeah, that one messed me up too. That’s why I put them side by side. Glad it helps :)

 

Nice quiz. Nailed it ♥

 

Wonderfull test. It made me doubt myself, although I got every one right!! 😝

I learned this mostly from FunFunFunction, I don't know what videos exactly but he made me understand it quite well.

 

This was a really great exercise Annie. Thank you for this.

 

Very well put , I always confuse the two! Using arrow and using none when dealing with 'this'

 
 
 

On the surface, 'this' seems like an intuitive keyword that simply refers to the environment (or scope) it resides in.

Note: It's actually context, not scope.

 

Ah, I see. So context is object-based, and scope is function-based (reference here: ryanmorr.com/understanding-scope-a...). Just corrected it. Thanks so much for clarifying!

 

Thanks for sharing, Annie. That was fun.

 

Thanks for the insight and quiz 😊😊😊

 

Very nice way of teaching. (Although I answered all correctly 😜😜)

 

It is good to see the efforts you have put.

 

Thanks... great article!

 

Very well illustrated 👏

 

This was pretty great! Thanks :)

 

4 of 4, all right.