The Golden Rule: this = whatever object is before the dot when the function is called

Step-by-Step Decision Tree:

1. Arrow Function? → No own this, inherits from parent scope

javascript

const obj = {
  name: "John",
  regular() {
    const arrow = () => console.log(this.name);
    arrow(); // "John" (inherits from regular method)
  }
};

2. Called with dot notation? (obj.method())this = object before dot

javascript

user.sayHi() // this = user
admin.greet() // this = admin
obj.nested.method() // this = obj.nested (whatever's before the dot)

3. Called without dot? (just function())this = undefined (strict) or window (non-strict)

javascript

function test() { console.log(this); }
test(); // undefined (strict mode) or window (non-strict)

4. Called with .call(), .apply(), .bind()?this = whatever you specify

javascript

func.call(obj) // this = obj
func.bind(obj)() // this = obj

5. Constructor with ‘new’?this = new object being created

javascript

new MyFunction() // this = newly created object

6. Event handler?this = element that triggered event

javascript

button.addEventListener('click', function() {
  console.log(this); // this = button element
});

Simple Memory Device:

“Look at HOW the function is called, not WHERE it’s defined”

javascript

const obj = {
  name: "John",
  greet() { console.log(this.name); }
};
 
const func = obj.greet; // extract the function
 
// Same function, different calls:
obj.greet();    // "John" (called with dot → this = obj)
func();         // undefined (called without dot → this = undefined)

Visual Pattern Recognition:

javascript

// Pattern: something.method() → this = something
user.sayHi()           // this = user
car.engine.start()     // this = car.engine
document.body.click()  // this = document.body
 
// Pattern: bareFunction() → this = undefined/window
sayHi()               // this = undefined (strict)
(function(){})()      // this = undefined (strict)
 
// Pattern: arrow function → this = inherited
() => {}              // this = from outer scope

Interview Gotchas with Solutions:

javascript

// Gotcha 1: Method extraction
const user = {
  name: "John",
  greet() { console.log(this.name); }
};
 
const greet = user.greet;
greet(); // undefined - no dot, so this = undefined
// Solution: user.greet() or greet.call(user)
 
// Gotcha 2: Callback functions
setTimeout(user.greet, 1000); // undefined - called without dot
// Solution: setTimeout(() => user.greet(), 1000)
 
// Gotcha 3: Event handlers
button.onclick = user.greet; // this = button (not user!)
// Solution: button.onclick = () => user.greet()
 
// Gotcha 4: Nested functions
const obj = {
  name: "John",
  outer() {
    function inner() { console.log(this.name); }
    inner(); // undefined - inner() called without dot
  }
};
// Solution: use arrow function or save this

Quick Checklist:

When you see a function call, ask:

  1. Is it an arrow function? → Inherits this
  2. Is there a dot before the function name?this = whatever’s before dot
  3. No dot?this = undefined (strict mode)

Memory Trick:

“this is determined by the CALL SITE, not the definition site”

javascript

function whoAmI() { return this; }
 
const obj1 = { name: "obj1", method: whoAmI };
const obj2 = { name: "obj2", method: whoAmI };
 
obj1.method(); // this = obj1 (called with obj1.)
obj2.method(); // this = obj2 (called with obj2.)
whoAmI();      // this = undefined (called without dot)

Bottom line: Look at how the function is CALLED. Dot before function name = this is whatever’s before the dot. No dot = this is undefined/window. Arrow functions inherit from parent!


Scope Chain vs this - Different Systems:

Scope chain: Where to find variables this binding: What object this refers to

javascript

const user = {
  name: "John",
  greet() { 
    console.log(name);      // Uses scope chain to find 'name'
    console.log(this.name); // Uses 'this' binding to find 'name'
  }
};

Why Scope Chain Doesn’t Help with this.name:

javascript

const user = {
  name: "John",
  greet() { console.log(this.name); }
};
 
const extracted = user.greet;
extracted();

What happens:

  1. Function looks for this.name
  2. First it needs to figure out what this is
  3. Since called without dot: this = undefined
  4. Then tries to access undefined.name → Error!

Scope chain is never used because we’re not looking for a variable name, we’re looking for this.name!

Let me show the difference:

javascript

const name = "Global Name"; // Global variable
 
const user = {
  name: "John",
  greet1() { 
    console.log(name);      // Uses scope chain → "Global Name"
  },
  greet2() { 
    console.log(this.name); // Uses this binding → "John" or undefined
  }
};
 
const extracted1 = user.greet1;
const extracted2 = user.greet2;
 
extracted1(); // "Global Name" (scope chain finds global 'name')
extracted2(); // undefined (this = undefined, so undefined.name)

Visual Explanation:

javascript

const user = {
  name: "John",
  greet() { 
    console.log(this.name); // 'this' is determined by HOW function is called
  }
};
 
// When you extract:
const greet = user.greet;
 
// The function becomes:
function greet() { 
  console.log(this.name); 
  // 'this' = ??? (determined at call time)
  // Scope chain can't help find 'this'!
}

Key Insight:

Scope chain is about lexical scope (where variables are defined) this is about execution context (how function is called)

javascript

function outer() {
  const localVar = "I'm local";
  
  return function inner() {
    console.log(localVar);    // ✅ Scope chain finds this
    console.log(this.name);   // ❌ Scope chain can't help with 'this'
  };
}
 
const func = outer();
func(); // "I'm local", undefined