Binding Priority & Lost `this`
The Pyramid and the Pitfalls
LinkedIn Hook
Four rules govern
thisin JavaScript. What happens when two rules fight each other?
newbeatsbind.bindbeats the dot. The dot beats nothing at all.And yet — the most common
thisbug in production has nothing to do with the priority. It's thatthisgoes missing when you hand a method off as a callback, store it in a variable, or pass it tosetTimeout.That's "lost
this", and it's the reason so many event handlers silently logundefinedinstead of the expected value.In this lesson I lay out the full priority pyramid, the 3 classic ways
thisgets 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
What You'll Learn
- The full 4-level priority order JavaScript uses when multiple
thisrules could apply - The 3 common scenarios where
thisgoes 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.objis to the left of the dot, sothis = obj.obj.greetArrow()— arrow function has no ownthis. The enclosing scope when the arrow was defined is the global/module scope (object literals don't create scope), sothis.nameisundefined.greet()— the function is extracted. There's no dot, no explicit binding, nonew. Default binding kicks in:this=window(orundefinedin strict mode).
Common Mistakes
- Storing a method in a variable (
const fn = obj.method) and callingfn()— you've stripped the dot, so implicit binding is gone andthisreverts to default. - Passing
obj.methoddirectly tosetTimeout,setInterval, oraddEventListenerwithout wrapping or binding — callbacks receive a plain function reference, not the method/owner pair. - Believing
bindcan be overridden bycall/applylater — 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:
newbinding > explicit binding (call/apply/bind) > implicit binding (dot notation) > default binding (global object, orundefinedin strict mode).
Q: How does this get "lost"?
thisis 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.
- 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., insideforEach(function() {...})) where the inner function has no dot owner.
Q: Name 3 fixes for lost this.
- 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 withnewignores the bound context and uses the freshly created object asthis.newsits 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.