this, call, apply & bind
Advanced · 18 min read · ▶ live playground · ✦ checkpoint
You can copy a method off an object and make it forget who it belongs to. That is the this trap: this is not where the function was written. It is chosen by how the function is called, and after this lesson you'll be able to predict what this will be and fix it on purpose.
The binding rules
A this binding is the value JavaScript gives to this for one function call. A call site is the exact expression that calls the function, such as player.label() or label(). Think of this as a badge handed to a function at the door; the call site decides which badge it gets.
The first two rules come from lesson 7.3:
- Implicit binding means
object.method()setsthisto the receiver, the object before the dot. - Default binding means a plain function call like
method()has no receiver. In this playground's script context, code runs like a plain browser script, so default binding usesglobalThis, the standard name for the global object.
globalThis.playerName = "Global guest";
globalThis.playerScore = 0;
const player = {
playerName: "Mina",
playerScore: 27,
label() {
return `${this.playerName}: ${this.playerScore}`;
},
};
const looseLabel = player.label;
console.log(player.label());
console.log(looseLabel());→ Mina: 27, then Global guest: 0.
player.label() has a receiver, so this is player. looseLabel() has no dot, so the function gets the default binding. The function body did not change. The call site changed.
call, apply, and bind
Explicit binding means you choose this directly instead of letting the call shape choose it. A regular function is a function declaration or function expression that creates its own this, and JavaScript gives regular functions three helpers:
callruns the function now with a chosenthis, then regular arguments.applyruns the function now with a chosenthis, then arguments from an array.bindreturns a new function withthisfixed for later.
function scoreLine(round, mood) {
return `${this.playerName}: ${round} score ${this.playerScore} (${mood})`;
}
const mina = {
playerName: "Mina",
playerScore: 42,
};
console.log(scoreLine.call(mina, "practice", "calm"));
console.log(scoreLine.apply(mina, ["final", "focused"]));
const minaBonusLine = scoreLine.bind(mina, "bonus");
console.log(minaBonusLine("ready"));→ Mina: practice score 42 (calm), Mina: final score 42 (focused), then Mina: bonus score 42 (ready).
Use call when you need one immediate borrowed call. Use apply when your arguments already live in an array. Use bind when another piece of code will call the function later and you need the badge to stay attached.
Losing this in callbacks
A callback is a function handed to another function so it can call it at the right moment. That handoff often removes the dot.
globalThis.playerName = "Global guest";
globalThis.playerScore = 0;
const tracker = {
playerName: "Rae",
playerScore: 10,
addBonus(points) {
this.playerScore = this.playerScore + points;
return `${this.playerName}: ${this.playerScore}`;
},
};
const bonuses = [2, 3];
const broken = bonuses.map(tracker.addBonus);
console.log(broken.join(" | "));
console.log(`real Rae score: ${tracker.playerScore}`);→ Global guest: 2 | Global guest: 5, then real Rae score: 10.
map calls the callback as a function. It does not call tracker.addBonus(...), so this is not tracker. Two common fixes are to bind once, or to wrap the method call in an arrow so the dot stays inside the callback.
const tracker = {
playerName: "Rae",
playerScore: 10,
addBonus(points) {
this.playerScore = this.playerScore + points;
return `${this.playerName}: ${this.playerScore}`;
},
};
const bonuses = [2, 3];
const boundAddBonus = tracker.addBonus.bind(tracker);
const fixedWithBind = bonuses.map(boundAddBonus);
const fixedWithArrow = bonuses.map(points => tracker.addBonus(points));
console.log(fixedWithBind.join(" | "));
console.log(fixedWithArrow.join(" | "));
console.log(`real Rae score: ${tracker.playerScore}`);→ Rae: 12 | Rae: 15, then Rae: 17 | Rae: 20, then real Rae score: 20.
The bind version fixes the method before map receives it. The arrow version does not need its own this; it calls tracker.addBonus(points) with the receiver still visible before the dot.
Try it — fix the lost method
Run the broken call first. Then replace session.finish inside map with either session.finish.bind(session) or points => session.finish(points).
new binding and arrow this
new binding happens when you call a function with new: JavaScript creates a fresh object and sets this to that object for the call. A constructor function is a regular function intended to be called with new to create an object.
function PlayerCard(playerName, playerScore) {
this.playerName = playerName;
this.playerScore = playerScore;
this.label = function () {
return `${this.playerName}: ${this.playerScore}`;
};
}
const card = new PlayerCard("Ira", 16);
console.log(card.label());→ Ira: 16.
You mostly meet this rule through classes in Section 11, but the rule is already here: new supplies the receiver-like object for the first call.
Arrow functions are the odd one out. Lexical this means an arrow function reuses this from the surrounding code instead of creating its own binding.
const coach = {
playerName: "Coach Imani",
makeReminder() {
return () => `${this.playerName} says hydrate`;
},
};
const reminder = coach.makeReminder();
console.log(reminder());→ Coach Imani says hydrate.
coach.makeReminder() sets this to coach. The arrow is born during that call, so it keeps that surrounding this for later. That is useful for callbacks. It is also why arrows are the wrong spelling for object methods that need their own receiver.
You now have the map: default, implicit, explicit, new, and arrow. Next, we move from who a function is called as to where values live while your program runs: stack, heap, reachability, and garbage collection.
Checkpoint
Answer all three to mark this lesson complete