this is one of JavaScript's most confusing keywords, and Node.js doesn't change the rules — but async code and callbacks are exactly where this bugs tend to surface, so it's worth being deliberate about it.
The rule, in one sentence
A regular function gets its own this, decided by how it's called. An arrow function has no this of its own — it borrows this from whatever scope it was written inside.
Where this bites people: callbacks inside object methods
const order = {
id: 'ORD-101',
items: ['pen', 'notebook', 'eraser'],
printItems() {
console.log(`Order ${this.id}:`);
this.items.forEach(function (item) {
// regular function — 'this' here is NOT the order object
console.log(this.id + ' -> ' + item); // this.id is undefined
});
}
};
order.printItems();
Inside printItems(), this correctly refers to order — because it's called as order.printItems(). But the plain function passed to .forEach() gets its own this, unrelated to order, so this.id inside it comes back undefined.
The fix: use an arrow function for the callback
const order = {
id: 'ORD-101',
items: ['pen', 'notebook', 'eraser'],
printItems() {
console.log(`Order ${this.id}:`);
this.items.forEach((item) => {
// arrow function — inherits 'this' from printItems()
console.log(this.id + ' -> ' + item);
});
}
};
order.printItems();
// Order ORD-101:
// ORD-101 -> pen
// ORD-101 -> notebook
// ORD-101 -> eraser
Because the arrow function doesn't create its own this, it simply reuses whatever this was at the moment it was written — which, here, is order.
A rule of thumb for choosing between them
- Defining a method on an object or class, where
thisshould refer to that object → use a regular function (or the shorthand method syntax shown above). - Writing a callback (for
.forEach(),.then(),setTimeout, event handlers) where you wantthisto stay whatever it was in the surrounding code → use an arrow function.
This single distinction resolves the large majority of "why is this undefined" confusion in real Node.js code.