Output-based: what does `obj.print()` log when print uses `this.value`
It depends entirely on HOW the method is called, not where it's defined. obj.print() → 'this' is obj. But pull the method off (const p = obj.print; p()) and 'this' is lost (undefined/global). Arrow methods capture lexical 'this', not obj. The lesson: 'this' is determined at call time.
Output-based this questions test one rule: **this is determined by how a function is called, not where it's defined.**
The baseline case
const obj = {
value: 42,
print() { console.log(this.value); },
};
obj.print(); // 42obj.print() — the method is called as a method of obj (obj. before the call), so this is obj, and this.value is 42.
The variations interviewers use to trip you up
1. Detached method — this is lost
const print = obj.print;
print(); // ❌ TypeError (strict mode) or undefined — 'this' is NOT objYou pulled the function off the object. There's no obj. at the call site → this is undefined (strict/modules) or the global object (sloppy). this.value throws or is undefined.
2. Passed as a callback — same problem
setTimeout(obj.print, 100); // 'this' lost — called bare later
[1].forEach(obj.print); // 'this' lost
button.addEventListener('click', obj.print); // 'this' becomes the elementPassing obj.print passes the bare function; whoever calls it later decides this.
3. Arrow function method — captures lexical this
const obj2 = {
value: 42,
print: () => console.log(this.value), // arrow → 'this' is the ENCLOSING scope, not obj2
};
obj2.print(); // undefined — 'this' is module/global scope, not obj2Arrow functions ignore the call site; this is whatever it was where the arrow was defined. So an arrow as an object method almost never does what you want.
4. Fixing it — bind/arrow wrapper
const bound = obj.print.bind(obj);
bound(); // 42 — 'this' permanently bound
setTimeout(() => obj.print(), 100); // 42 — called as a method inside the arrowThe rule (the actual answer)
this is set at call time by the call pattern:
obj.method()→this=obj(implicit binding).fn()bare →this=undefined(strict) / global (sloppy).fn.call/apply/bind(x)→this=x(explicit).new Fn()→this= the new instance.- Arrow function →
this= lexical, ignores all of the above.
How to answer
"It depends on how print is called. obj.print() logs 42 — implicit binding makes this the object. But const p = obj.print; p() loses this — undefined or global — because there's no object at the call site. If print were an arrow function, this would be the lexical scope, not obj, so it'd be wrong even called as obj.print(). The principle: this is bound at call time by the call pattern, not at definition."
Follow-up questions
- •Why does const p = obj.print; p() lose the 'this' binding?
- •What happens if print is an arrow function instead?
- •How do bind/call/apply change the answer?
- •What is 'this' when a method is used as an event listener?
Common mistakes
- •Assuming 'this' is fixed by where the method is defined.
- •Passing obj.method as a callback and expecting 'this' to stay the object.
- •Using an arrow function as an object method and expecting 'this' to be the object.
- •Forgetting strict mode makes a bare call's 'this' undefined (not global).
Performance considerations
- •
Edge cases
- •Method passed to setTimeout/forEach/event listener.
- •Arrow function method.
- •Nested functions inside a method (inner regular function loses 'this').
- •Strict mode vs sloppy mode difference.
Real-world examples
- •The classic 'this is lost' bug when passing a class method as a React event handler without binding.