Finally Understand this in JavaScript — The Mental Model That Actually Sticks
Functions, arrows, events, classes, and React — once you see the pattern, this finally makes sense
The Frontend Daily is a reader-supported publication. Subscribe to get more real-world JavaScript deep dives every week.
✅ 1. Why this
Feels So Confusing
Every JavaScript developer hits this
bugs early:
"
this
is undefined""
this
refers to window""
this
suddenly changes inside callbacks""
this
works differently in arrow functions""
this
inside timers gives unexpected results""
this
in event handlers behaves differently in different contexts"
👉 The core problem:
In many languages, this
refers to the object that owns the method.
But in JavaScript, this
refers to how the function is called — not where it’s written.
That’s why this
can seem to work fine in one situation and completely break in another, even inside the same function.
👉 One simple rule to remember:
Always ask: Who called the function?
The answer will almost always explain where this
points.
If you internalize this one rule, 95% of this
confusion disappears — even in complex scenarios.
🧠 2. The Real Mental Model
Ask yourself: Who is calling the function?
That caller decides what this
points to.
🔎 Example:
const obj = {
name: 'Frontend Daily',
greet() {
console.log(this.name);
}
};
obj.greet(); // 👉 'Frontend Daily'
Here, obj
is calling greet()
. So this
refers to obj
.
But now watch:
const greet = obj.greet;
greet(); // 👉 undefined (or window in sloppy mode)
We pulled out
greet
and called it without context.No object is calling it — so
this
becomesundefined
(orwindow
in non-strict mode).
👉 Extracted methods lose this
.
🚀 3. Arrow Functions — The Major Exception
Arrow functions don’t have their own this
.
Instead, they "inherit" this
from where they are defined.
🔎 Example:
const obj = {
name: 'Frontend Daily',
greet: () => {
console.log(this.name);
}
};
obj.greet(); // 👉 undefined
The arrow function doesn’t bind
this
toobj
.It looks for
this
in its parent scope (usually global or module scope).
👉 Tip:
Arrows are great for callbacks when you want to preserve this
automatically.
🔑 Summary so far:
If called as:
obj.method()
→ this refers to:obj
If called as:
method()
→ this refers to:undefined
(orwindow
in sloppy mode)If called as: Bound function (
method.bind(obj)()
)
→ this refers to: The bound contextIf using: Arrow function
→ this refers to: Lexical parent scope (where it was defined)
⚠️ 4. Classic Mistakes with Event Handlers
Event handlers show this behavior clearly:
Using normal function:
button.addEventListener('click', function() {
console.log(this); // 👉 button element
});
Using arrow function:
button.addEventListener('click', () => {
console.log(this); // 👉 outer scope
});
👉 This often trips up new developers:
Normal functions bind
this
to the DOM element.Arrows don’t — they just capture the parent scope.
🔄 5. Controlling this
Explicitly
A. .bind()
You can lock this
manually:
function greet() {
console.log(this.name);
}
const obj = { name: 'Frontend Daily' };
const boundGreet = greet.bind(obj);
boundGreet(); // 👉 'Frontend Daily'
bind()
returns a new function that always uses the provided this
.
B. Arrow functions (again)
Most modern React and frontend code prefers arrow functions because they eliminate 90% of this
headaches.
No binding. No surprises. Just lexical scoping.
🧨 6. Interview Landmines — Where this
Breaks People
Inside timers like
setTimeout()
Inside array methods (
map()
,forEach()
)When passing functions as callbacks
When extracting methods out of objects
🔎 Example:
const obj = {
name: 'Frontend Daily',
delayedGreet() {
setTimeout(function() {
console.log(this.name); // 👉 undefined
}, 1000);
}
};
obj.delayedGreet();
this
inside setTimeout
refers to the global scope — not obj
.
✅ Fixed with arrow:
setTimeout(() => {
console.log(this.name);
}, 1000);
Now this
remains obj
, because the arrow function preserves its lexical context.
⚛️ 7. Why this
Matters in React (Even With Hooks)
In modern React (functional components + hooks), we almost never directly deal with this
— that’s one of the reasons hooks were introduced.
But it's still very useful to understand how this
works behind the scenes, especially if:
You’re reading older code (class components)
You’re debugging external libraries
You accidentally mix patterns
✅ Functional Components — No this
With functional components, everything is just functions — no special binding needed:
function Counter() {
const [count, setCount] = React.useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>{count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
👉 Notice:
No
this
insideincrement
.We don't need to bind anything.
Arrow functions preserve the lexical scope automatically.
🚫 What If We Accidentally Use Normal Functions?
If you wrote:
function Counter() {
const [count, setCount] = React.useState(0);
function increment() {
console.log(this); // ❌ undefined
setCount(count + 1);
}
return <button onClick={increment}>Increment</button>;
}
Here,
this
isundefined
because it's a plain function inside a module scope.But because React passes no special binding, you’re safe unless you accidentally try to rely on
this
.
👉 This is why arrow functions inside hooks are safe defaults.
🔥 React Classes (for comparison)
Older React class components had to bind this
manually:
class Counter extends React.Component {
constructor() {
super();
this.state = { count: 0 };
this.increment = this.increment.bind(this); // 👈 binding required
}
increment() {
this.setState({ count: this.state.count + 1 });
}
render() {
return <button onClick={this.increment}>Increment</button>;
}
}
Without
.bind(this)
in constructor,this
insideincrement
would be undefined when called.
💡 Summary for React Devs
Hooks and functional components mostly eliminate
this
problems.But understanding
this
is still valuable when:You debug class-based libraries
You work with event listeners inside useEffect
You use external callbacks (non-React)
🧵 8. Summary Mental Model
✅ In JavaScript, this
is dynamic — based on how you call the function.
✅ Arrow functions lock this
from their lexical scope.
✅ When in doubt:
Use arrow functions for callbacks.
Bind methods in class components.
Be careful when passing extracted methods.
✅ In React:
Hooks solve most
this
problems.Still useful to fully understand for legacy code or interviews.
Wow, this is a niche. Keep up the good work, keep getting your stuff out there!