RoadmapDay 6 / 80
JavaScript CoreMonth 1 · Week 2

Day 6: this Keyword Mechanics, Call, Apply, Bind & Arrow Functions

Stop guessing what `this` refers to — learn the deterministic rule set the engine follows, and how arrow functions opt out of it entirely.

Mark this day complete

Study

Concepts

this is determined at call time, not definition time

Unlike lexical scope, `this` for a normal function is decided by HOW it is called, not where it is written. The engine checks four rules in priority order: (1) `new Fn()` → `this` is the newly created object. (2) `fn.call(obj)` / `fn.apply(obj)` / `boundFn = fn.bind(obj)` → `this` is explicitly set to `obj`. (3) `obj.method()` → `this` is `obj` (whatever is left of the dot at call time). (4) Plain call `fn()` → `this` is `undefined` in strict mode (or the global object in sloppy mode).

This is why passing `obj.method` as a callback (e.g. to `setTimeout` or as an event handler) loses `this` — you detached the function from `obj` before calling it, so rule 4 kicks in.

Arrow functions have no this of their own

Arrow functions do not create their own `this` binding at all — they capture `this` lexically from the enclosing scope at definition time, exactly like a regular variable via the scope chain. call/apply/bind cannot override an arrow function's `this`.

This makes arrows ideal for callbacks nested inside methods (they inherit the outer `this` automatically) but wrong for object methods or prototype methods that need a dynamic `this` per instance.

See It

Visualizations

Visualization

Regular function vs arrow function `this`

 function () {}() => {}
Has own this binding?Yes — set per callNo — inherited lexically
Affected by call/apply/bind?Yes, fullyNo — ignored
Affected by obj.method() call style?Yes — this = objNo — still outer this
Good for object methods?YesNo — this ≠ the object
Good for nested callbacks?Needs .bind() or a saved varYes — inherits automatically

Build It

Code Examples

The four this-binding rules, demonstrated

js
const user = {
  name: 'Ali',
  greetMethod() {
    console.log('method call:', this.name); // rule 3: this = user
  },
};

function GreetNew(name) {
  this.name = name; // rule 1: this = the new object
}

function greetPlain() {
  console.log('plain call:', this); // rule 4: undefined (strict mode)
}

user.greetMethod();                       // 'method call: Ali'
const detached = user.greetMethod;
// detached();                             // TypeError: this.name of undefined

const bound = user.greetMethod.bind(user); // rule 2
bound();                                   // 'method call: Ali'

new GreetNew('Sara');                      // rule 1 in action

'use strict';
greetPlain();                              // 'plain call: undefined'

Fixing lost this in a class, three ways

js
class Timer {
  seconds = 0;

  // Fix 1: class field arrow function — bound once, per instance, automatically
  tickArrow = () => {
    this.seconds += 1;
  };

  // Regular method — needs manual binding if used as a callback
  tickMethod() {
    this.seconds += 1;
  }

  start() {
    // Fix 2: bind in the constructor/caller
    setInterval(this.tickMethod.bind(this), 1000);
    // Fix 3: wrap in an arrow at the call site
    setInterval(() => this.tickMethod(), 1000);
    // No fix needed — already bound
    setInterval(this.tickArrow, 1000);
  }
}

Remember

Key Takeaways

  • this for a normal function is resolved at CALL time via 4 ranked rules: new > bind/call/apply > obj.method() > plain call.
  • Detaching a method from its object (passing it as a bare reference) loses this — this is the #1 real-world this bug.
  • Arrow functions never have their own this — they read it from the enclosing lexical scope, permanently.
  • call/apply invoke immediately with a given this; bind returns a new function permanently bound to that this.
  • Class field arrow functions are the modern idiom for auto-bound event handlers/callbacks in components.

Do It

Practice

  1. 1Predict this for 6 different call styles of the same function (plain, method, bind, arrow, new, setTimeout) before running them.
  2. 2Refactor a class with a broken onClick handler (loses this) using each of the three fixes above.
  3. 3Implement your own Function.prototype.myBind from scratch using call/apply and closures.