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() sets this to 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 uses globalThis, 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:

  • call runs the function now with a chosen this, then regular arguments.
  • apply runs the function now with a chosen this, then arguments from an array.
  • bind returns a new function with this fixed 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).

javascript — live consolereal engine
⌘/Ctrl + Enter to run

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

+50 XP on completion