JavaScript Interview Prep
this Keyword & Binding

Binding Priority & Lost `this`

The Pyramid and the Pitfalls

LinkedIn Hook

Four rules govern this in JavaScript. What happens when two rules fight each other?

new beats bind. bind beats the dot. The dot beats nothing at all.

And yet — the most common this bug in production has nothing to do with the priority. It's that this goes missing when you hand a method off as a callback, store it in a variable, or pass it to setTimeout.

That's "lost this", and it's the reason so many event handlers silently log undefined instead of the expected value.

In this lesson I lay out the full priority pyramid, the 3 classic ways this gets lost, and the 3 bullet-proof fixes every JavaScript developer should know by reflex.

Read the full lesson -> [link]

#JavaScript #InterviewPrep #ThisKeyword #BindingPriority #LostThis #CodingInterview #Frontend


Binding Priority & Lost this thumbnail


What You'll Learn

  • The full 4-level priority order JavaScript uses when multiple this rules could apply
  • The 3 common scenarios where this goes missing in real-world code
  • The 3 reliable fixes for "lost this" — bind, arrow wrapper, and regular wrapper

The Priority Order

When multiple rules could apply, JavaScript follows this priority (highest to lowest):

1. new Binding         (highest priority)
2. Explicit Binding    (call / apply / bind)
3. Implicit Binding    (dot notation)
4. Default Binding     (lowest — global or undefined in strict mode)

Proving the Priority

Explicit > Implicit:

const obj = {
  name: "Rakibul",
  greet: function() {
    console.log(this.name);
  }
};

const other = { name: "Karim" };

obj.greet();            // "Rakibul" — implicit
obj.greet.call(other);  // "Karim"  — explicit overrides implicit

new > Explicit:

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

const boundPerson = Person.bind({ name: "Karim" });

// Even though bind set this to { name: "Karim" }, new overrides it
const p = new boundPerson("Rakibul");
console.log(p.name); // "Rakibul" — new wins over bind!

The "Lost this" Problem

This is one of the most common JavaScript bugs. this gets "lost" when a method is separated from its object.

Scenario 1: Extracting a Method

const obj = {
  name: "Rakibul",
  greet: function() {
    console.log(this.name);
  }
};

obj.greet(); // "Rakibul" — implicit binding works

const greet = obj.greet; // extract the function
greet(); // undefined — 'this' is now global/undefined (no dot!)

Scenario 2: Passing a Method as a Callback

const obj = {
  name: "Rakibul",
  greet: function() {
    console.log(this.name);
  }
};

setTimeout(obj.greet, 1000); // undefined — method is passed as a plain function reference

// Same problem with event handlers
// button.addEventListener("click", obj.greet); // 'this' = button, not obj

Scenario 3: Assigning to Another Variable in a Loop

const handlers = {
  name: "Handler",
  methods: ["click", "hover", "scroll"],
  registerAll: function() {
    this.methods.forEach(function(method) {
      console.log(this.name + " registers " + method);
      // 'this' is undefined/global inside regular callback!
    });
  }
};

handlers.registerAll();
// undefined registers click
// undefined registers hover
// undefined registers scroll

3 Ways to Fix Lost this

Fix 1: Use bind

setTimeout(obj.greet.bind(obj), 1000); // "Rakibul"

Fix 2: Use an Arrow Function Wrapper

setTimeout(() => obj.greet(), 1000); // "Rakibul"

Fix 3: Use a Wrapper Function (old school)

setTimeout(function() {
  obj.greet();
}, 1000); // "Rakibul"

The Classic Tricky Interview Question

const obj = {
  name: "Rakibul",
  greet: function() { console.log(this.name); },
  greetArrow: () => { console.log(this.name); }
};

obj.greet();      // "Rakibul" — implicit binding, this = obj
obj.greetArrow(); // undefined — arrow function, this = enclosing scope (global/module)

const greet = obj.greet;
greet();          // undefined — lost this, plain function call, default binding

Explanation:

  • obj.greet() — implicit binding. obj is to the left of the dot, so this = obj.
  • obj.greetArrow() — arrow function has no own this. The enclosing scope when the arrow was defined is the global/module scope (object literals don't create scope), so this.name is undefined.
  • greet() — the function is extracted. There's no dot, no explicit binding, no new. Default binding kicks in: this = window (or undefined in strict mode).

Binding Priority & Lost this visual 1


Common Mistakes

  • Storing a method in a variable (const fn = obj.method) and calling fn() — you've stripped the dot, so implicit binding is gone and this reverts to default.
  • Passing obj.method directly to setTimeout, setInterval, or addEventListener without wrapping or binding — callbacks receive a plain function reference, not the method/owner pair.
  • Believing bind can be overridden by call/apply later — it cannot. Once bound, a function is locked; the later call is silently ignored.

Interview Questions

Q: What is the binding priority order for this?

From highest to lowest: new binding > explicit binding (call/apply/bind) > implicit binding (dot notation) > default binding (global object, or undefined in strict mode).

Q: How does this get "lost"?

this is lost when a method is separated from its object — either by extracting it into a variable (const fn = obj.method), passing it as a callback (setTimeout(obj.method)), or invoking it without the original dot notation.

Q: Name 3 scenarios where this is lost.

  1. Extracting a method into a standalone variable. 2) Passing a method as a callback to setTimeout, setInterval, or an event listener. 3) Invoking the method inside a regular-function callback (e.g., inside forEach(function() {...})) where the inner function has no dot owner.

Q: Name 3 fixes for lost this.

  1. Use .bind(obj) to permanently pin the context. 2) Wrap in an arrow function: () => obj.method(). 3) Wrap in a regular function: function() { obj.method(); }.

Q: Which wins — new or bind?

new. Even if a function has been bound with .bind(someObj), calling it with new ignores the bound context and uses the freshly created object as this. new sits at the top of the priority pyramid.


Quick Reference — Cheat Sheet

this BINDING PRIORITY (HIGH -> LOW)

  1. new Foo()                    -> this = brand new object
  2. fn.call / apply / bind(obj)  -> this = obj
  3. obj.fn()                     -> this = obj (left of the dot)
  4. fn()                         -> window (non-strict) / undefined (strict)

Arrow functions:
  No own this. Always lexical (enclosing scope).
  Cannot be overridden by call / apply / bind / new.

LOST this — 3 scenarios:
  * const fn = obj.method; fn();          // extracted
  * setTimeout(obj.method, 1000);          // callback
  * arr.forEach(function() { this.x });    // inner fn

LOST this — 3 fixes:
  * obj.method.bind(obj)           // permanent
  * () => obj.method()             // arrow wrapper
  * function() { obj.method(); }   // regular wrapper

Previous: Arrow Functions & this -> Lexical Binding Next: The Prototype Chain


This is Lesson 4.5 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.

On this page