this Context Traps

Hard100% Free~30 mins#this#bind#arrow-functions#implicit-binding#output-prediction#javascript-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};
11
12const boundA = user.getId.bind({ id: 'worker' });
13const boundB = boundA.bind({ id: 'guest' });
14
15const res1 = user.getId();
16const res2 = user.getNested();
17const res3 = boundB();
18const res4 = boundA.call({ id: 'admin' });
19
20console.log(res1, res2, res3, res4);
Predict Console Output
Interactive Challenge

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.

Call Stack (Top = Active)
Global Execution Context
Lexical Scope / Bindings
user:{ id: 'master', getId: f, getNested: f }
Console Stream
> [empty]

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:

  1. Default Binding: Standalone function invocation (fn()). In strict mode, this is undefined. In non-strict mode, this defaults to globalThis (window in browsers).
  2. Implicit Binding: Method invocation (obj.fn()). The object immediately preceding the dot becomes this.
  3. Explicit Binding: Direct invocation with .call(), .apply(), or .bind().
  4. new Binding: Constructor invocation (new Fn()). this refers 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 any this passed 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 with obj2, but the inner wrapper passes its own obj1 to the underlying function. The first bind ALWAYS wins!
javascript
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?