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 = obj5. Constructor with ‘new’? → this = new object being created
javascript
new MyFunction() // this = newly created object6. 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 scopeInterview 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 thisQuick Checklist:
When you see a function call, ask:
- Is it an arrow function? → Inherits
this - Is there a dot before the function name? →
this= whatever’s before dot - 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:
- Function looks for
this.name - First it needs to figure out what
thisis - Since called without dot:
this = undefined - 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