Common 'this' pitfalls

JavaScript

The short answer

The most common this pitfalls are: losing this when passing methods as callbacks, using arrow functions as object methods, this in setTimeout/setInterval, and this in nested functions. All of these happen because this depends on how a function is called, not where it is defined (except for arrow functions).

Pitfall 1: Passing a method as a callback

This is the most common one. When you take a method off an object and pass it somewhere else, this is no longer the object.

const user = {
name: 'John',
greet() {
console.log(`Hello, ${this.name}`);
},
};
user.greet(); // "Hello, John" — works fine
setTimeout(user.greet, 1000); // "Hello, undefined" — broken!

When you write setTimeout(user.greet, 1000), you are passing just the function, not the object. setTimeout calls it as a plain function, so this becomes window (or undefined in strict mode).

Fix: Use bind or wrap it in an arrow function:

setTimeout(user.greet.bind(user), 1000);
// or
setTimeout(() => user.greet(), 1000);

Pitfall 2: Arrow functions as object methods

Arrow functions do not have their own this. If you use an arrow function as an object method, this will not be the object.

const user = {
name: 'John',
greet: () => {
console.log(`Hello, ${this.name}`);
},
};
user.greet(); // "Hello, undefined"

The arrow function takes this from the outer scope (which is the global scope here), not from the user object.

Fix: Use a regular function for object methods:

const user = {
name: 'John',
greet() {
console.log(`Hello, ${this.name}`);
},
};

Pitfall 3: this in setTimeout/setInterval

Inside setTimeout, a regular function gets window (or undefined) as this:

const timer = {
seconds: 0,
start() {
setInterval(function () {
this.seconds += 1; // this is window, not timer!
console.log(this.seconds); // NaN
}, 1000);
},
};

Fix: Use an arrow function inside — it inherits this from start:

const timer = {
seconds: 0,
start() {
setInterval(() => {
this.seconds += 1; // this is timer
console.log(this.seconds); // 1, 2, 3...
}, 1000);
},
};

This is one case where arrow functions are exactly what you want. The start method is a regular function (so this is timer), and the arrow function inside inherits that this.

Pitfall 4: Nested functions

A regular function inside another function does not inherit this:

const user = {
name: 'John',
friends: ['Jane', 'Bob'],
showFriends() {
this.friends.forEach(function (friend) {
console.log(`${this.name} knows ${friend}`);
// this.name is undefined — this is window
});
},
};

The callback inside forEach is a regular function, so it gets default binding (window or undefined).

Fix 1: Use an arrow function:

this.friends.forEach((friend) => {
console.log(`${this.name} knows ${friend}`); // works
});

Fix 2: Save this to a variable (older pattern):

showFriends() {
const self = this;
this.friends.forEach(function (friend) {
console.log(`${self.name} knows ${friend}`);
});
}

The const self = this pattern was common before arrow functions existed. You might still see it in older codebases.

Pitfall 5: Destructuring methods from an object

const user = {
name: 'John',
greet() {
console.log(`Hello, ${this.name}`);
},
};
const { greet } = user;
greet(); // "Hello, undefined" — this is lost

Destructuring extracts the function, but the this context is gone. It is the same as writing const greet = user.greet — you have the function but not the binding.

Common Pitfalls

The general rule to remember: whenever you separate a method from its object (by assigning it to a variable, passing it as a callback, or destructuring it), you lose the this binding. You need to either use .bind(), wrap it in an arrow function, or use arrow function class properties.

Interview Tip

If the interviewer asks about this pitfalls, pick two or three from this list and walk through them with code. Show the broken code first, explain why it is broken, and then show the fix. The most important ones to know are passing methods as callbacks and using arrow functions vs regular functions. These come up in real code every day.

Why interviewers ask this

this bugs are some of the most common JavaScript bugs in production code. Interviewers ask about these pitfalls to see if you have dealt with them before, if you can spot the issue quickly, and if you know multiple ways to fix it. A candidate who knows these pitfalls writes fewer bugs and can debug faster.