this Keyword in JavaScript: call, apply, bind and Arrow Functions with Interview Traps

Determine this from the call site, compare explicit binding methods, and see why arrows behave differently in callbacks and object methods.

KnowledgeGate Team

Exam prep & CS education

Updated 17 Aug 20265 min read

this confuses candidates because its value usually depends on how a function is called, not where that function was written. If you learn only the obj.method() case, detached methods, callbacks, and arrows all look unpredictable. A small precedence rule makes their output traceable.

The four this-binding rules, by precedence

For ordinary functions, inspect the call site and apply the highest matching rule:

  1. new binding: A constructor call creates a fresh object and uses it as this.

  2. Explicit binding: call, apply, or bind supplies this directly.

  3. Implicit binding: In obj.method(), the object immediately before the dot is this.

  4. Default binding: A bare fn() call has this === undefined in strict mode. In a classic non-strict browser script, it can fall back to the global object.

Arrow functions sit outside this ladder. They do not create their own this; they inherit it from the surrounding lexical scope. call, apply, bind, and new cannot assign an arrow its own receiver.

Precedence ladder for JavaScript this binding: new, call/apply/bind, object method, then default; arrows inherit this lexically.

When rules appear to compete, precedence resolves the result. A bound ordinary function called with new uses the fresh constructed object, while an explicit call overrides an object that might otherwise provide implicit binding.

One reliable interview habit is to ignore the definition site first. Rewrite the final invocation alone, identify its call syntax, and only then inspect the function body.

Implicit binding and the lost-this trap

Start with a method call:

const obj = {
  x: 10,
  get() {
    return this.x;
  }
};

obj.get(); // 10

The call site is obj.get(), so implicit binding makes this equal to obj.

Now detach the same function:

const g = obj.get;
g();

The call site is now a bare g(). In strict mode, this is undefined, so trying to read this.x throws a TypeError. In a sloppy browser script, this may be window; if window.x is absent, the expression returns undefined. The method did not remember obj. Passing an unbound method as a callback creates the same lost-receiver problem.

Explicit binding with call, apply and bind

All three APIs select a receiver for an ordinary function. Their difference is how arguments arrive and whether invocation happens now.

function greet(greeting) {
  return greeting + " " + this.name;
}

const person = { name: "Asha" };

call invokes immediately and accepts arguments one by one:

greet.call(person, "Hi"); // "Hi Asha"

apply invokes immediately but accepts the arguments in an array-like collection:

greet.apply(person, ["Hi"]); // "Hi Asha"

bind does not invoke immediately. It returns a new function whose receiver is fixed:

const boundGreet = greet.bind(person);
boundGreet("Hi"); // "Hi Asha"

The calculation in every case is the same: this.name contributes "Asha", so "Hi" + " " + "Asha" produces "Hi Asha". bind is especially useful when handing a method to code that will later make a bare callback call.

Remember that bind returns a new function. It does not modify greet, and later calling the original still follows its own call site.

Arrow functions use lexical this

An arrow captures this from the surrounding scope where the arrow is created. That is useful for a callback nested inside a correctly called method:

const timer = {
  seconds: 0,
  start() {
    setInterval(() => {
      this.seconds++;
    }, 1000);
  }
};

timer.start();

timer.start() gives the ordinary start method implicit binding, so this inside start is timer. The arrow then closes over that same value. Each interval callback adds 1 to timer.seconds; after one callback it is 1, after two it is 2, and after three it is 3.

A regular function callback would receive its this according to how the timer system invokes it, not from start. Do not assume that receiver is timer.

Lexical capture has a flip side. An arrow is usually the wrong choice for an object method that needs the object as its receiver:

const user = {
  name: "Asha",
  getName: () => this.name
};

user.getName() does not make the arrow's this equal to user. The arrow keeps the outer this. The same warning applies to prototype methods. Use ordinary method syntax when the call site should select the receiver.

new binding creates the receiver

Constructor-style calls apply the highest rule:

function Person(name) {
  this.name = name;
}

const p = new Person("Asha");
p.name; // "Asha"

new creates an object linked to Person.prototype, binds it as this, runs the function, and normally returns that object. Calling Person("Asha") without new is a bare call instead. In strict mode, assigning this.name then throws because this is undefined; sloppy code can accidentally write a global property.

Modern code can use a class when construction is intended, but interviews still use constructor functions to test whether new changes the receiver.

Interview traps involving this

  • A detached method loses implicit binding.

  • A nested regular function inside a method gets its own receiver from its own call site.

  • setTimeout(obj.method, 0) passes a function, not an obj.method() call. Bind it or wrap it in an arrow that calls the method.

  • Event systems may deliberately set this to an element or another dispatcher-specific value. Check that API rather than assuming your object.

  • An arrow used as a method does not acquire the object receiver.

  • call or apply on an arrow can pass arguments but cannot replace its lexical this.

  • Rebinding an already bound function does not replace the receiver fixed by the first bind.

The setTimeout case is worth tracing once, because the fix depends on which receiver you want:

const counter = {
  n: 5,
  show() {
    console.log(this.n);
  }
};

setTimeout(counter.show, 0);               // undefined
setTimeout(() => counter.show(), 0);       // 5
setTimeout(counter.show.bind(counter), 0); // 5

The first line hands show to the timer as a plain function value, so the timer chooses the receiver. Host environments differ on what they pass (a browser supplies the global object, Node supplies its own timer object) and neither carries an n property, so this.n reads undefined. The arrow keeps the real call site as counter.show(), and bind fixes the receiver before the timer ever sees the function, so both of those print 5.

Practise these output patterns alongside JavaScript interview questions for freshers and React interview questions for freshers. React class callbacks and ordinary JavaScript methods expose the same call-site principle.

The short version and next step

For an ordinary function, find the call site and apply new, explicit, implicit, or default binding in that order. Arrows instead inherit this lexically. call and apply invoke now, while bind returns a fixed function for later.

Two habits carry most of the marks here: name the rule you applied, and say what happens in strict mode as well as in a sloppy script. Use the Placement Preparation category for the broader route, build the language model in Complete JavaScript, or follow the Mera Placement Hoga bundle. On each snippet, circle the call site before predicting the output.