Arrow functions don’t play by the same rules as traditional functions — here’s the why, the how, and the real-world impact.

Introduction: The “Wait, Why Undefined?” Bug
Imagine this: you’re building a simple object with a greet
method.
const user = {
name: "Ali",
greet: () => `Hi, I’m ${this.name}`
};
console.log(user.greet());
// "Hi, I'm undefined" 🤯
If you’re new to JavaScript, this is a WTF moment.
Why does this.name
not work here, but works fine with a normal function?
The answer lies in how arrow functions handle this
differently from traditional functions.
This isn’t just trivia — it affects your React components, Node.js code, event listeners, and async callbacks. Let’s break it down.
1. Traditional Functions: this
is Dynamic
In a traditional function, this
is determined by how you call the function.
const user = {
name: "Sara",
greet: function () {
return `Hi, I’m ${this.name}`;
}
};
console.log(user.greet());
// "Hi, I'm Sara"
✅ Works fine because this
is set to the object calling the method.
2. Arrow Functions: this
is Lexical
Arrow functions don’t create their own this
. Instead, they inherit this
from their surrounding scope.
const user = {
name: "Sara",
greet: () => `Hi, I’m ${this.name}`
};
console.log(user.greet());
// "Hi, I'm undefined"
Here, this
is not the user
object. It’s inherited from the outer scope (global or module) — which doesn’t have a name
.
💡 Think of it like this:
- Traditional functions → “Who called me? That’s my
this
.” - Arrow functions → “I’ll just use my parent’s
this
.”
3. Why This Is Actually Useful
This “lexical this
” behavior solves one of JavaScript’s longest-standing headaches: losing this
inside callbacks.
Example: Timer with Regular Function (❌ Broken)
function Timer() {
this.seconds = 0;
setInterval(function () {
this.seconds++; // ❌ 'this' is global
}, 1000);
}
Example: Timer with Arrow Function (✅ Works)
function Timer() {
this.seconds = 0;
setInterval(() => {
this.seconds++; // ✅ uses Timer's this
}, 1000);
}
const t = new Timer();
setTimeout(() => console.log(t.seconds), 3100);
// ~3
No more const self = this
or .bind(this)
hacks.
4. When Arrow Functions Break Things
While arrows fix callbacks, they break object methods and event handlers.
Example: DOM Event with Arrow (❌ Wrong)
const button = document.querySelector("button");
button.addEventListener("click", () => {
console.log(this); // ❌ Window
});
Example: DOM Event with Regular Function (✅ Correct)
button.addEventListener("click", function () {
console.log(this); // ✅ Button element
});
💡 Rule of Thumb:
- Use arrow functions when you want lexical
this
. - Use regular functions when you want dynamic
this
.
5. Real-World Examples Where This Matters
In React Components
Arrows help keep this
clean in class components:
class App extends React.Component {
state = { count: 0 };
increment = () => this.setState({ count: this.state.count + 1 });
render() {
return <button onClick={this.increment}>Click</button>;
}
}
No need for .bind(this)
in the constructor.
In Node.js Modules
Arrows let you safely use this
inside nested functions without rebinding.
Wrapping It All Up
Here are the key truths about this
in arrow functions:
- Traditional functions bind
this
dynamically (depends on how called). - Arrow functions inherit
this
from the outer scope (lexical). - This solves callback problems (
setTimeout
,map
,React
). - But it breaks methods and event handlers where you want dynamic
this
. - The best developers know when to pick arrows vs regular.
👉 Bottom line: Arrow functions are not just shorthand — they change the rules of this
.
Call to Action
👉 Have you ever been bitten by the “undefined this
” bug? Share your story in the comments 👇.
📤 Send this to a teammate who always forgets how this
works.
🔖 Bookmark this — it’s the quick guide you’ll need during debugging.
Leave a Reply