intermediate

The this Keyword in JavaScript: All Four Binding Rules

Master this-binding for interviews — default, implicit, explicit, and new binding, arrow functions, lost this in callbacks, and the precedence order.


Why this trips everyone up

this is not resolved when a function is defined — it's resolved when the function is called. The same function can have a different this on every invocation. Interviewers exploit exactly this gap: they show a method passed as a callback and ask what it logs. If you know the four binding rules and their precedence, every this question becomes mechanical.

Rule 1: Default binding

A plain function call gets the global object as this (or undefined in strict mode):

function show() {
  console.log(this);
}
 
show(); // window (browser, sloppy mode) — undefined in strict mode / modules

Remember: ES modules are always strict, so in any modern codebase the default binding is effectively undefined.

Rule 2: Implicit binding

Call a function through an object, and this is that object — determined by the call site, not where the function lives:

const user = {
  name: "Ada",
  greet() {
    return `Hi, ${this.name}`;
  },
};
 
user.greet(); // "Hi, Ada" — called through user

The classic trap is losing the implicit binding by detaching the method:

const greet = user.greet;
greet();                      // "Hi, undefined" — plain call, default binding
 
setTimeout(user.greet, 0);    // same bug: the callback is a plain call

This "lost this" bug is the reason bind exists, and it's why interviewers ask you to implement call, apply, and bind from scratch.

Rule 3: Explicit binding

call, apply, and bind set this directly:

function greet(punctuation) {
  return `Hi, ${this.name}${punctuation}`;
}
 
const ada = { name: "Ada" };
 
greet.call(ada, "!");        // "Hi, Ada!"  — arguments listed
greet.apply(ada, ["!"]);     // "Hi, Ada!"  — arguments as array
const bound = greet.bind(ada);
bound("?");                  // "Hi, Ada?"  — permanently bound copy

Two details that earn senior points:

  • A bind-ed function cannot be re-bound — later call/apply can't change its this.
  • Passing null/undefined to call/apply falls back to default binding (globalThis in sloppy mode).

Rule 4: new binding

Calling a function with new creates a fresh object, sets it as this, links its prototype, and returns it (unless the function returns its own object):

function User(name) {
  this.name = name;
}
 
const u = new User("Ada"); // this = the newly created object

How new interacts with the prototype chain is its own interview topic — see prototypal inheritance.

Precedence

When rules collide, the order is:

new binding > explicit binding (bind/call/apply) > implicit binding > default binding.

function who() { console.log(this.name); }
 
const a = { name: "a", who };
const b = { name: "b" };
 
a.who();                 // "a"        implicit
a.who.call(b);           // "b"        explicit beats implicit
const boundA = who.bind(a);
boundA.call(b);          // "a"        bind can't be overridden by call
new (who.bind(a))();     // undefined  new beats even bind (fresh object)

Arrow functions: no this at all

Arrow functions don't have their own this — they close over the this of the enclosing scope, like any other variable (a closure over this):

const timer = {
  seconds: 0,
  start() {
    setInterval(() => {
      this.seconds++;        // works: arrow inherits start()'s this
    }, 1000);
  },
};

Replace the arrow with a regular function and this.seconds breaks — that's the single most common this interview snippet. Corollaries worth stating:

  • call/apply/bind have no effect on an arrow function's this.
  • Arrows are unusable as methods when you need the receiver (obj.method()), and as constructors (new throws).

The output-prediction question, assembled

const obj = {
  name: "outer",
  regular() {
    console.log("regular:", this.name);
    const arrow = () => console.log("arrow:", this.name);
    arrow();
 
    function inner() {
      console.log("inner:", this?.name);
    }
    inner();
  },
};
 
obj.regular();
// regular: outer   — implicit binding
// arrow: outer     — lexical this from regular()
// inner: undefined — plain call, default binding

If you can narrate that snippet rule-by-rule, you've passed the this portion of any interview.

Follow-ups to be ready for

  • "How would you fix a lost this in a callback?" — arrow wrapper, bind, or capture the value you need before passing the function.
  • "What is this at the top level of a module?"undefined in ESM; module.exports in CommonJS.
  • "What about this in class fields?" — class field arrows (handleClick = () => {}) bind per-instance; convenient for event handlers, but each instance gets its own function object.

  • The Event Loop

    intermediate

    How the call stack, microtask queue, and macrotask queue decide execution order — with the output-prediction questions interviewers love.

  • Closures

    beginner

    What a closure really is, why loops with var trip people up, and how closures power memoization, once(), and module patterns.

  • The prototype chain, __proto__ vs prototype, and what class syntax actually does under the hood.