completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

The this keyword

Learn how JavaScript decides this from the call site: default, implicit, explicit, and new binding, their precedence, and strict-mode edge cases.

By the end, you can
  • 01
    Read the call siteFind the place where a function is called, and use it to tell what this is.
  • 02
    Pick the rule that winsName the rule for a plain call, a call through an object, call, bind, and new, and say which rule wins when two fit.
  • 03
    Know what strict mode changesExplain why a plain call gives undefined in strict mode, and what happens to a number passed as this.

Inside a regular function, the word this stands for an object. A regular function is one written with the function keyword, or as a method in an object. It is not an arrow function. Which object is this? That depends on how you call the function.

You met the simple case in Methods & this. In user.sayHi(), this is user, the object before the dot. This lesson gives you the full set of rules.

The most important idea is the call site. The call site is the place in your code where a function is called, like user.sayHi(). To find this, don't look at where the function was written. Look at where it is called. The same function can get a different this each time you call it in a different way.

One-line definition

For a regular function, the call site decides this. There are four rules: default, implicit, explicit, and new. When more than one rule fits a call, a fixed order decides which rule wins.

Real-life analogythis is like the word “my”

If Ada says “my laptop,” the word “my” means Ada's laptop. If Grace says the same sentence, “my” means Grace's laptop. The sentence is the same. The speaker is different. this works like the word “my”. It means whoever is “speaking” at the moment the function is called.

In real life: “My notebook” means a different notebook for each speaker
In JavaScript: this.label gives a different label for each call
In real life: The person speaking right now
In JavaScript: The call site, which picks this
In real life: Where the sentence was written down doesn't decide whose notebook it is
In JavaScript: Where a regular function is written doesn't decide this

This lesson uses regular functions everywhere. Arrow functions are different. An arrow function has no this of its own, so the four rules below don't apply to it. The lesson Arrow functions & lexical this covers arrows.

Call-site rules: read the call, not the function

INTERACTIVE

A call site is the exact piece of code that calls a function. It can take four shapes:

  • fn(): a plain call, with no object.
  • obj.fn(): a call through an object.
  • fn.call(obj): a call that names the object to use.
  • new Fn(): a call that builds a new object.

JavaScript looks at the shape of the call site. It picks this before the function's code starts to run.

Square brackets still keep the objectPop out in the code editor (opens in a new tab)JavaScript
const cart = {
  label: "tea",
  show() {
    return this.label;
  },
};
console.log(cart["show"]());

Lines 1 to 6 make a cart object with a label and a show method. Line 7 calls the method with square brackets: cart["show"](). The object cart still comes right before the method name. So this is cart, and the line prints tea. Square brackets only change how you write the method name. They don't change which object becomes this.

Now try the trainer below. Each choice is a short program. Pick one, guess the rule, then step through it. The answers come from running the real code.

Who is this? Try each call
Step 0 of 2Ready
Your turn: follow the blue line

Pick a call. Step once to see the call site. Step again to see the this that real JavaScript used.

Running in
  1. script
Next: line 7
const ada = {  label: "Ada",  whoAmI() {    return this.label;  },};
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Pick a call to run.

Picking a new call starts the replay again. Guess the rule before you step.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Two details are worth remembering:

  • In building.room.whoAmI(), this is building.room, not building. Only the object right before the method name counts.
  • If you save a method in a plain variable and then call fn(), there is no dot any more. That is a new call site, with no object.
The call building.room.whoAmI(). The building cart holds a room card. An arrow leads to the room cart, which holds the whoAmI function. Under the room cart: this = room. Under the building cart: skipped.building.room.whoAmI()roombuildingskippedwhoAmI()roomthis = room
Read the call from the method name backward. The object right before whoAmI is building.room, so that object is this. building is only the way to get there. The call returns Nested room.

The panel below shows every call from the trainer at once. Pick a call to see its code, its result, and the rule that set this.

Real answers for each call
The call you pickedPop out in the code editor (opens in a new tab)JavaScript
const ada = {  label: "Ada",  whoAmI() {    return this.label;  },};ada.whoAmI();
Real resultimplicit binding
Returned
Ada
this is
Ada
Why
The call site is `ada.whoAmI()`. The object right before the method name is `ada`, so `ada` becomes `this`.
Try it yourself

Real JavaScript returned Ada. The rule is implicit binding, so this is Ada.

These answers come from running the code. The lesson's tests run the same code to check them.

Default, implicit, explicit & new binding

RULE TABLE

Setting this for a call is called binding. Regular functions have four binding rules. Each rule matches one shape of call:

  • Default binding: a plain call, like fn(). There is no object.
  • Implicit binding: a call through an object, like obj.fn(). The object becomes this.
  • Explicit binding: call, apply, or bind name the object you want. You choose this yourself.
  • new binding: new Fn() builds a brand-new object, and that object becomes this.

The table puts the four rules side by side. Read each row as: when the call looks like this, this becomes that.

The four rules that set this
RuleWhat the call looks likeWhat this becomesDoes strict mode change it?
DefaultA plain call, like fn().undefined in strict mode. globalThis in sloppy mode.Yes. This is the rule strict mode changes most.
ImplicitA call through an object, like obj.fn() or obj["fn"]().The object right before the method name.No. It works the same in both modes.
Explicitfn.call(obj), fn.apply(obj), or a bound function.The value you name. A bound function keeps the value it was given.A little. Strict mode keeps a primitive, like 5, as it is.
newnew Fn().A fresh object that can use the methods on Fn.prototype.No. The function gets a new object either way. Classes always use strict mode.
Four calls, each with a box called this. whoAmI() puts undefined in the box. ada.whoAmI() puts ada in it. whoAmI.call(grace) puts grace in it. new Reporter() puts a new object in it.whoAmI()undefinedthisdefault ruleada.whoAmI()adathisimplicit rulewhoAmI.call(grace)gracethisexplicit rulenew Reporter()new objectthisnew rule
The shape of the call decides what goes into this. A plain call leaves it undefined in strict mode. A call through an object uses that object. call uses the object you name. new uses a brand-new object.
Real-life analogyFour ways to hold the microphone

Think of the function as a speech written on paper. The call site decides who holds the microphone while the speech is read. In a plain call, nobody holds it. In a method call, the object holds it. With call, apply, or bind, you hand it to someone you choose. With new, a brand-new speaker is made before the speech starts.

In real life: Nobody is holding it
In JavaScript: Default binding: fn()
In real life: The owner picks it up
In JavaScript: Implicit binding: obj.fn()
In real life: Someone hands it to Grace
In JavaScript: Explicit binding: fn.call(grace)
In real life: A new speaker steps up just for this speech
In JavaScript: New binding: new Fn()
Which binding rule?
  • whoAmI()
  • ada.whoAmI()
  • team.lead.whoAmI()
  • whoAmI.call(grace)
  • bound.call(other)
  • new Reporter()
  • new BoundReporter()
  • const fn = ada.whoAmI; fn()
Try it yourself
0 of 8 correct

Put each call into the rule that sets this.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Explicit binding gets its own lesson next: call, apply & bind. For now, here is what each one does:

  • fn.call(obj, a, b) calls the function now. It uses obj as this and passes the arguments one by one.
  • fn.apply(obj, [a, b]) does the same, but takes the arguments in an array.
  • fn.bind(obj) doesn't call anything yet. It gives back a new function that always uses obj as this. That new function is called a bound function.

Binding precedence: which rule wins

STEP THROUGH

Most call sites fit just one rule. A few fit two rules at once. Then JavaScript uses a fixed order, called precedence. The stronger rule wins:

  1. new beats a bound function.
  2. A bound function beats a later call or apply.
  3. call or apply beats a call through an object.
  4. A call through an object beats a plain call.
A ladder of five rules, from strongest at the top to weakest at the bottom: new Fn(), bound fn, call / apply, obj.fn(), plain fn(). An arrow points up toward the strongest.new Fn()bound fncall / applyobj.fn()plain fn()strongestweakest
When two rules fit the same call, the higher one wins. new is at the top. A plain call is at the bottom: it only applies when nothing else does.
Which rule wins?
Step 0 of 6Ready
Your turn: follow the blue line

When two rules fit, use this order: new, then bound, then call/apply, then a call through an object, then a plain call.

Running in
  1. script
Next: line 15
function show() {  "use strict";  return this.label;}function Reporter() {  this.label = "new reporter";}Reporter.prototype.show = show; const ada = { label: "Ada", show };const grace = { label: "Grace" };const bound = show.bind({ label: "Bound target" });const BoundReporter = Reporter.bind({ label: "Bound target" }); bound.call(grace);new BoundReporter().show();
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The replay shows three calls where two rules fit. Each line has one winner:

  • Line 15, ada.show.call(grace): the code reads show from ada, but call(grace) wins. It returns Grace.
  • Line 16, bound.call(grace): bound already has its this fixed. The later call(grace) can't change it. It returns Bound target.
  • Line 17, new BoundReporter().show(): new still builds a fresh object, even for a bound function. It returns new reporter.
A quick way to check

Ask these questions in order. Stop at the first yes.

  1. Is there new? Then this is the new object.
  2. Is it a bound function? Then this is the object it was bound to.
  3. Does the call use call or apply? Then this is the value you passed.
  4. Is there an object right before the method name? Then this is that object.

If every answer is no, it is default binding.

this in strict mode

REAL OUTPUT

Strict mode is a stricter set of JavaScript rules. You turn it on with the line "use strict". Code without it runs in sloppy mode, the older, looser rules. Strict mode changes only two things about this:

  • A plain call. Strict mode leaves this as undefined. Sloppy mode puts globalThis there instead. globalThis is the global object: the big object that holds global names like console.
  • A primitive value as this. A primitive is a simple value, like the number 5. With call(5), strict mode keeps this as the plain number. Sloppy mode first wraps it in a Number object. This wrapping is called boxing.
Left: strictPlain() in strict mode. The box called this holds undefined. Right: sloppyPlain() in a sloppy script. The box called this holds globalThis.strictPlain()undefinedthisstrict modesloppyPlain()globalThisthissloppy script
Both are plain calls with no object. In strict mode, this stays undefined. In an old sloppy script, JavaScript puts globalThis there instead.
Strict mode vs sloppy mode
Step 0 of 4Ready
Your turn: follow the blue line

Strict mode doesn't change calls through an object, call, bind, or new. It only changes plain calls and primitive values like 5.

Running in
  1. script
Next: line 16
function strictPlain() {  "use strict";  return this;}function sloppyPlain() {  return this;}function strictNumber() {  "use strict";  return typeof this;}function sloppyNumber() {  return typeof this;} console.log(sloppyPlain() === globalThis);console.log(strictNumber.call(5));console.log(sloppyNumber.call(5));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

This page's own code always runs in strict mode, so it can't show sloppy mode directly. The panel below runs a small old-style script in a separate, locked-down frame. A frame like this is called an iframe. The panel shows what the script reports.

Sloppy mode in a real browser
The old-style script in the iframePop out in the code editor (opens in a new tab)HTML
<!doctype html><meta charset="utf-8"><script>function plain() { return this === globalThis; }function primitive() { return typeof this; }parent.postMessage({ kind: "this-keyword-sloppy", plain: plain(), primitive: primitive.call(5) }, "*");</script>
What the iframe reportssloppy mode

Waiting for the old-style script to report…

Try it yourself
Reset runs the script again.

Waiting for the old-style script to report…

The iframe is locked down. It can only run this fixed script, not code you type.

The table compares the two modes. Read each row as: for this call, here is this in each mode.

Plain calls and primitive values in strict and sloppy mode
When you writeIn strict modeIn a sloppy script
plain()this is undefined.this is globalThis.
plain.call(null)this is null.this becomes globalThis.
plain.call(5)this is the number 5.this is a Number object that wraps 5.
Code in a module or a classIt always runs in strict mode.This can't happen. Modules and classes are always strict.

This site's lessons are written as ES modules. A module is a JavaScript file that can import and export code. Modules always use strict mode. Class bodies always use strict mode too. So the sloppy results above come from the lesson's tests and the iframe, not from this page's own code.

Where you'll use this

You'll meet this in objects, classes, array methods, event handlers, and many libraries. Here are the patterns you'll see most.

Fix a method saved in a variablePop out in the code editor (opens in a new tab)JavaScript
const user = {  label: "Ada",  getLabel() { return this.label; },};const getLabel = user.getLabel;console.log(getLabel.call(user));

Line 5 copies the method into a plain variable, getLabel. Calling getLabel() on its own would be a plain call, with no object. Line 6 calls getLabel.call(user) instead. That names user as this, so it prints Ada. The lesson Losing this goes deeper into this problem.

Borrow an array method for another objectPop out in the code editor (opens in a new tab)JavaScript
const cartLike = { 0: "book", 1: "pen", length: 2 };const joined = Array.prototype.join.call(cartLike, " + ");console.log(joined);

Line 1 makes an object that looks like an array. It has the keys 0 and 1, and a length. An object like this is called array-like. It has no join method of its own. Line 2 borrows join from arrays and calls it with cartLike as this. Line 3 prints book + pen. Using another object's method like this is called method borrowing.

Callbacks, classes, and browser handlers

A callback is a function you pass to another function, which calls it later. A callback doesn't get a special this just because you passed it. The function that calls it decides. Here is what happens in three common cases:

  • Some array methods take an extra argument called thisArg, like array.forEach(fn, thisArg). They use it as this for the callback.
  • A class method still uses the object before the dot.
  • A browser event listener gets its element as this, but only if the listener is a regular function.
forEach can set this for a callbackPop out in the code editor (opens in a new tab)JavaScript
const user = { name: "Asha" };
function greet() {
  console.log(this.name);
}
[1].forEach(greet, user);

Line 1 makes a user object. Lines 2 to 4 define a regular function that prints this.name. Line 5 passes greet to forEach, with user as the second argument. The array [1] has one item, so the callback runs once. forEach uses its second argument as this, so it prints Asha. This is a feature of forEach, not a fifth rule.

An arrow callback keeps the method's thisPop out in the code editor (opens in a new tab)JavaScript
const user = {
  name: "Asha",
  showName() {
    [1].forEach(() => console.log(this.name));
  },
};
user.showName();

Line 7 calls user.showName(), so inside showName, this is user. Line 4 passes an arrow function to forEach. An arrow has no this of its own, so it uses the this of showName, which is user. It prints Asha. This is why arrows are handy for callbacks. A regular callback would need thisArg or call to get the right this.

Class methods still use the call sitePop out in the code editor (opens in a new tab)JavaScript
class Profile {
  constructor(name) {
    this.name = name;
  }
  label() {
    return this.name;
  }
}
const profile = new Profile("Asha");
console.log(profile.label());

Lines 2 to 4 are the constructor. On line 9, new builds a fresh object, and the constructor stores name on it. Line 10 calls profile.label(), so this is profile, and it prints Asha. Class bodies always use strict mode, but a call through an object still works the normal way.

A regular event listener gets its element as thisHTML
<!doctype html>
<button id="save">Save</button>
<script>
  const button = document.querySelector("#save");
  button.addEventListener("click", function () {
    console.log(this.id);
  });
</script>

Line 2 makes a button with the id save. Line 5 adds a click listener. The listener is a regular function, so when you click, this is the button. Line 6 prints this.id, which is save. An arrow function listener would not get the button as this. It would use the this from the code around it. This example needs a real web page, so the editor shows it but can't run it.

In a module, top-level this is undefinedJavaScript
console.log(typeof this);

Top-level code is code that isn't inside any function. In a module, top-level this is undefined, so typeof this is "undefined" and the line prints undefined. This is a rule for modules. It is not the default binding of a function call. The lesson editor runs old-style scripts, not modules, so this example isn't run here. Browsers and Node run it as a module, for example from a .mjs file.

Common misconceptions

  • “this is the object the function belongs to.” Only for a call through an object, like obj.fn(). If you save the method in a variable and call it on its own, the object is gone.
  • “With nested objects, the outer object is this.” Only the object right before the method name counts. In building.room.whoAmI(), that is building.room.
  • “call can change the this of a bound function.” A bound function keeps its this. A later call can't change it.
  • “Strict mode breaks methods.” Strict mode changes only plain calls and primitive values. A call through an object still uses the object before the dot.
  • “Arrow functions follow these four rules.” An arrow has no this of its own. It uses the this of the code around it.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upPredict a call through an object and a plain call

Type the two printed lines in order.

Starter codePop out in the code editor (opens in a new tab)JavaScript
"use strict";
function show() { return this?.label ?? String(this); }
const ada = { label: "Ada", show };
const fn = ada.show;
console.log(ada.show());
console.log(fn());

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeFix a method saved in a variable

    The method is saved in getLabel. Make the call on line 6 still read from user, then type what it prints.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const user = {
      label: "Ada",
      getLabel() { return this.label; },
    };
    const getLabel = user.getLabel;
    console.log(getLabel.call(user));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeBorrow an array method

      Use call to join the items of an array-like object.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const cartLike = { 0: "book", 1: "pen", length: 2 };
      const joined = Array.prototype.join.call(cartLike, " + ");
      console.log(joined);

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · ChallengePredict new on a bound function

        Which label wins when you call a bound function with new?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        function Reporter() { this.label = "new reporter"; }
        Reporter.prototype.show = function show() { return this.label; };
        const BoundReporter = Reporter.bind({ label: "bound target" });
        console.log(new BoundReporter().show());

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeCompare plain calls in strict and sloppy mode

          Type the two true-or-false values the comparisons print.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          function strictPlain() { "use strict"; return this; }
          function sloppyPlain() { return this; }
          console.log(strictPlain() === undefined);
          console.log(sloppyPlain() === globalThis);

          Answer, then press Check. Spacing and letter case don’t matter.

            Check your understanding

            7 QUESTIONS
            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7For a regular function, what decides the value of this?

              Choose an answer to see the explanation.

            2. Question 2 of 7The method is saved in fn and called on its own. What does it print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              "use strict";
              function show() { return this?.label ?? String(this); }
              const ada = { label: "Ada", show };
              const fn = ada.show;
              console.log(fn());

              Choose an answer to see the explanation.

            3. Question 3 of 7In profile.name.toUpperCase.call(other), which rule sets this for toUpperCase?

              Choose an answer to see the explanation.

            4. Question 4 of 7What does new on a bound function print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              function Reporter() { this.label = "new reporter"; }
              Reporter.prototype.show = function show() { return this.label; };
              const BoundReporter = Reporter.bind({ label: "bound target" });
              console.log(new BoundReporter().show());

              Choose an answer to see the explanation.

            5. Question 5 of 7In strict mode, what is this inside a plain call like fn()?

              Choose an answer to see the explanation.

            6. Question 6 of 7What does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              function show() { "use strict"; return typeof this; }
              console.log(show.call(5));

              Choose an answer to see the explanation.

            7. Question 7 of 7Which order is correct, from the strongest rule to the weakest?

              Choose an answer to see the explanation.

            Key takeaways

            • For a regular function, the call site decides this.
            • There are four rules: default, implicit, explicit, and new binding.
            • When two rules fit, the order is: new, then a bound function, then call/apply, then a call through an object, then a plain call.
            • In strict mode, a plain call leaves this as undefined, and a primitive stays a primitive.
            • Arrow functions are different. They use the this of the code around them.

            Final definition: this is the object a regular function gets from the way it is called. Arrow functions are the exception: they use the this of the code around them.

            Up next: call, apply & bind.

            CompleteFrontend Clear concepts. Working examples.