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.
- 01Read the call siteFind the place where a function is called, and use it to tell what
thisis. - 02Pick the rule that winsName the rule for a plain call, a call through an object,
call,bind, andnew, and say which rule wins when two fit. - 03Know what strict mode changesExplain why a plain call gives
undefinedin strict mode, and what happens to a number passed asthis.
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.
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.
this 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.labelgives 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
INTERACTIVEA 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.
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.
Pick a call. Step once to see the call site. Step again to see the this that real JavaScript used.
script
const ada = { label: "Ada", whoAmI() { return this.label; },};Two details are worth remembering:
- In
building.room.whoAmI(),thisisbuilding.room, notbuilding. 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.
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.
const ada = { label: "Ada", whoAmI() { return this.label; },};ada.whoAmI();- 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`.
Real JavaScript returned Ada. The rule is implicit binding, so this is Ada.
Default, implicit, explicit & new binding
RULE TABLESetting 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 becomesthis. - Explicit binding:
call,apply, orbindname the object you want. You choosethisyourself. newbinding:new Fn()builds a brand-new object, and that object becomesthis.
The table puts the four rules side by side. Read each row as: when the call looks like this, this becomes that.
| Rule | What the call looks like | What this becomes | Does strict mode change it? |
|---|---|---|---|
| Default | A plain call, like fn(). | undefined in strict mode. globalThis in sloppy mode. | Yes. This is the rule strict mode changes most. |
| Implicit | A 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. |
| Explicit | fn.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. |
| new | new 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. |
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.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()
whoAmI()ada.whoAmI()team.lead.whoAmI()whoAmI.call(grace)bound.call(other)new Reporter()new BoundReporter()const fn = ada.whoAmI; fn()
Put each call into the rule that sets this.
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 usesobjasthisand 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 usesobjasthis. That new function is called a bound function.
Binding precedence: which rule wins
STEP THROUGHMost 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:
newbeats a bound function.- A bound function beats a later
callorapply. callorapplybeats a call through an object.- A call through an object beats a plain call.
new is at the top. A plain call is at the bottom: it only applies when nothing else does.When two rules fit, use this order: new, then bound, then call/apply, then a call through an object, then a plain call.
script
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();The replay shows three calls where two rules fit. Each line has one winner:
- Line 15,
ada.show.call(grace): the code readsshowfromada, butcall(grace)wins. It returnsGrace. - Line 16,
bound.call(grace):boundalready has itsthisfixed. The latercall(grace)can't change it. It returnsBound target. - Line 17,
new BoundReporter().show():newstill builds a fresh object, even for a bound function. It returnsnew reporter.
Ask these questions in order. Stop at the first yes.
- Is there
new? Thenthisis the new object. - Is it a bound function? Then
thisis the object it was bound to. - Does the call use
callorapply? Thenthisis the value you passed. - Is there an object right before the method name? Then
thisis that object.
If every answer is no, it is default binding.
this in strict mode
REAL OUTPUTStrict 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
thisasundefined. Sloppy mode putsglobalThisthere instead.globalThisis the global object: the big object that holds global names likeconsole. - A primitive value as
this. A primitive is a simple value, like the number5. Withcall(5), strict mode keepsthisas the plain number. Sloppy mode first wraps it in a Number object. This wrapping is called boxing.
this stays undefined. In an old sloppy script, JavaScript puts globalThis there instead.Strict mode doesn't change calls through an object, call, bind, or new. It only changes plain calls and primitive values like 5.
script
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));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.
<!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>Waiting for the old-style script to report…
Waiting for the old-style script to report…
The table compares the two modes. Read each row as: for this call, here is this in each mode.
| When you write | In strict mode | In 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 class | It 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.
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.
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, likearray.forEach(fn, thisArg). They use it asthisfor 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.
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.
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 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.
<!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.
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
- “
thisis the object the function belongs to.” Only for a call through an object, likeobj.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. Inbuilding.room.whoAmI(), that isbuilding.room. - “
callcan change thethisof a bound function.” A bound function keeps itsthis. A latercallcan'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
thisof its own. It uses thethisof the code around it.
Practice exercises
5 EXERCISESType the two printed lines in order.
"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());Line 5 calls ada.show(), so this is ada and it prints Ada. Line 6 calls fn() with no object. The code is in strict mode, so this is undefined. Then this?.label is undefined, and String(this) prints the text undefined.
The method is saved in getLabel. Make the call on line 6 still read from user, then type what it prints.
const user = {
label: "Ada",
getLabel() { return this.label; },
};
const getLabel = user.getLabel;
console.log(getLabel.call(user));const user = {
label: "Ada",
getLabel() { return this.label; },
};
const getLabel = user.getLabel;
console.log(getLabel.call(user));getLabel.call(user) names user as this, so the method reads user.label and prints Ada.
Use call to join the items of an array-like object.
const cartLike = { 0: "book", 1: "pen", length: 2 };
const joined = Array.prototype.join.call(cartLike, " + ");
console.log(joined);const cartLike = { 0: "book", 1: "pen", length: 2 };
const joined = Array.prototype.join.call(cartLike, " + ");
console.log(joined);join doesn't need a real array. It only needs a this with the keys 0 and 1 and a length. It joins the two items with " + " between them.
Which label wins when you call a bound function with new?
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());new BoundReporter() builds a fresh object and uses it as this. Reporter writes label onto that object, so show() returns new reporter.
Type the two true-or-false values the comparisons print.
function strictPlain() { "use strict"; return this; }
function sloppyPlain() { return this; }
console.log(strictPlain() === undefined);
console.log(sloppyPlain() === globalThis);In strict mode, a plain call leaves this as undefined, so the first line prints true. In sloppy mode, a plain call gets globalThis, so the second line prints true too.
Check your understanding
7 QUESTIONSQuestion 1 of 7For a regular function, what decides the value of
this?Choose an answer to see the explanation.
Question 2 of 7The method is saved in
fnand called on its own. What does it print?Read the code, then predict"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.
Question 3 of 7In
profile.name.toUpperCase.call(other), which rule setsthisfortoUpperCase?Choose an answer to see the explanation.
Question 4 of 7What does
newon a bound function print?Read the code, then predictfunction 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.
Question 5 of 7In strict mode, what is
thisinside a plain call likefn()?Choose an answer to see the explanation.
Question 6 of 7What does this print?
Read the code, then predictfunction show() { "use strict"; return typeof this; } console.log(show.call(5));Choose an answer to see the explanation.
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
newbinding. - When two rules fit, the order is:
new, then a bound function, thencall/apply, then a call through an object, then a plain call. - In strict mode, a plain call leaves
thisasundefined, and a primitive stays a primitive. - Arrow functions are different. They use the
thisof 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.