this Context Traps
Key Learning Objectives
Master the 4 fundamental rules of JavaScript this binding: Default, Implicit, Explicit, and new.
Understand why Function.prototype.bind creates an irreversible hard-bound exotic function object.
Learn why chaining multiple .bind() calls never overrides the original bound this argument.
Understand why .call() and .apply() are completely ignored by hard-bound functions.
Master lexical this inheritance in arrow functions and why arrow functions ignore explicit rebinding.
The Interview Problem
What is logged to the console when the following object methods, arrow functions, and bound functions are invoked, and why does chained binding preserve the original target context?
1const user = {2 id: 'master',3 getId() {4 return this.id;5 },6 getNested() {7 const inner = () => this.id;8 return inner();9 }10};1112const boundA = user.getId.bind({ id: 'worker' });13const boundB = boundA.bind({ id: 'guest' });1415const res1 = user.getId();16const res2 = user.getNested();17const res3 = boundB();18const res4 = boundA.call({ id: 'admin' });1920console.log(res1, res2, res3, res4);
Predict Console Output
Select the option that matches what standard ECMAScript prints to the console:
master master worker worker
master undefined guest admin
master master guest worker
master master worker admin
V8 Engine Execution Trace
Step 1 of 8 (Line 1)Initializes user object in heap memory with methods getId and getNested.
Deep Technical Breakdown
The 4 Rules of JavaScript this
In JavaScript, this is not bound to a function's declaration location (except for arrow functions); it is determined dynamically by how the function is called at the call-site:
- Default Binding: Standalone function invocation (
fn()). In strict mode,thisisundefined. In non-strict mode,thisdefaults toglobalThis(windowin browsers). - Implicit Binding: Method invocation (
obj.fn()). The object immediately preceding the dot becomesthis. - Explicit Binding: Direct invocation with
.call(),.apply(), or.bind(). newBinding: Constructor invocation (new Fn()).thisrefers to the newly created instance object.
The ECMAScript Bound Function Invariant
When Function.prototype.bind(thisArg) is executed, the JavaScript engine creates a Bound Function Exotic Object (§ 9.4.1 in ECMAScript specification):
- It stores
[[BoundTargetFunction]]and[[BoundThis]]in internal slots. - Whenever the bound function is invoked, its internal
[[Call]]method calls the target function with[[BoundThis]], completely ignoring anythispassed to.call()or.apply(). - If you chain
.bind():fn.bind(obj1).bind(obj2), the second bind wraps the first bound function. When called, the outer wrapper calls the inner wrapper withobj2, but the inner wrapper passes its ownobj1to the underlying function. The first bind ALWAYS wins!
function greet() {
return this.name;
}
const a = greet.bind({ name: 'First' });
const b = a.bind({ name: 'Second' });
console.log(b()); // 'First' (First bind wins!)
console.log(a.call({ name: 'Override' })); // 'First' (call ignored!)Arrow Functions: Lexical Scoping
Arrow functions do not have their own this binding at all. When this is referenced inside an arrow function, the engine resolves it just like any regular variable by searching up the lexical scope chain to the nearest enclosing non-arrow function or global environment. Consequently, methods like .call(), .apply(), and .bind() have zero effect on an arrow function's this.
Common Traps & Mistakes
Thinking that calling `.bind()` twice will update `this` to the second argument. Once a function is bound, its `this` is immutable.
Attempting to use `.call()` or `.apply()` on an arrow function to set its context. Arrow functions have no `this` slot and ignore explicit context.
Expecting object literal properties to establish lexical `this` for arrow functions: `{ name: 'Foo', arrow: () => this.name }`. Object literals do NOT create a lexical scope; the arrow function inherits `this` from the outer context (e.g. window).
Confusing method detachment (`const fn = obj.method; fn();`) with bound invocation. Detached methods lose implicit binding and revert to default binding.
FAANG Follow-Up Probes
Probe #1
Can the `new` operator override a bound function's `[[BoundThis]]`? (Yes! The ECMAScript specification explicitly allows `new BoundFn()` to create a new instance, bypassing `[[BoundThis]]`).
Probe #2
How would you implement a polyfill for Function.prototype.bind in ES5 that handles partial argument application and new instantiation?
Probe #3
Why does using arrow functions as class methods in React components increase memory consumption compared to prototype methods?
