Everyone learns the shortcut: this is "the object before the dot." Call user.save() and inside save, this is user. That's true often enough to get you through a tutorial, and then it breaks the first time you pass a method somewhere as a callback and this is suddenly undefined.
I've debugged that exact bug more than once. Here's the actual rule.
Keyword this is set by the call, not the definition
Where you write a function has nothing to do with what this is inside it. What matters is how the function is called. There are four call patterns, checked roughly in this order:
new Thing()--thisis the brand-new object being constructed.obj.method()--thisisobj, the thing left of the dot at the call site.fn.call(x)/fn.apply(x)/fn.bind(x)--thisis whatever you passed asx.fn()with nothing else --thisisundefinedin strict mode (and modules are always strict), or the global object in sloppy mode.
Same function, four different this values, depending only on the call. That's the whole concept.
"What does this refer to in an arrow function inside a method?"
We'll get there, but you have to see the bug first.
The bug: a method that loses its object
const user = {
name: "Ada",
greet() {
console.log("hi, " + this.name);
},
};
user.greet(); // "hi, Ada" -- called with the dot
const fn = user.greet;
fn(); // "hi, undefined" -- no dot, no object
setTimeout(user.greet, 100); // "hi, undefined" -- same thing
fn and user.greet are the same function. But fn() has nothing to the left of it at the call site, so the implicit obj.method() rule doesn't apply, and this falls back to undefined. setTimeout does the same thing: it stores the function and later calls it plain.
The fix, before arrow functions
Bind the object on for good:
setTimeout(user.greet.bind(user), 100); // "hi, Ada"
bind returns a new function with this permanently locked to user, no matter how it's later called.
Arrow functions don't have their own this
This is the part the interview question is really about. An arrow function doesn't get a this from its call site at all. It looks up this in the scope where it was written, lexically, like any other variable.
An arrow inside a method
const user = {
name: "Ada",
greetLater() {
setTimeout(() => {
console.log("hi, " + this.name); // "hi, Ada"
}, 100);
},
};
user.greetLater();
greetLater is called with the dot, so its this is user. The arrow inside it has no this of its own, so it uses greetLater's this, which is user. That's why you never need .bind(this) or a const self = this line inside a method any more, the arrow just carries the surrounding this in.
So: an arrow inside a method, where the method was called as obj.method(), has this equal to obj.
An arrow as a method
Flip it around and it stops working:
const user = {
name: "Ada",
greet: () => {
console.log("hi, " + this.name); // "hi, undefined"
},
};
user.greet();
user.greet() has the dot, but greet is an arrow, so it ignores that and looks outward. At the top level of a module there's no meaningful this, so this.name is undefined. Don't use arrow functions for object methods that need this. Use them for callbacks inside methods, which is the opposite case.
Where this bit every React developer
Class components are the reason a generation of developers learned this the hard way:
class Button extends React.Component {
handleClick() {
this.setState(/* ... */); // `this` is undefined when passed as onClick
}
render() {
return <button onClick={this.handleClick}>...</button>;
}
}
this.handleClick gets handed to the button and later called plain, so this is gone. The fixes were this.handleClick = this.handleClick.bind(this) in the constructor, or writing handleClick = () => {} as a class field so it's an arrow that captures the instance. Hooks removed the problem entirely, there's no this in a function component.

call, apply, bind in one line each
call-- invoke now, passthisand args one by one:fn.call(obj, a, b)apply-- invoke now, passthisand args as an array:fn.apply(obj, [a, b])bind-- don't invoke, return a new function withthis(and optionally some args) fixed:const bound = fn.bind(obj)
Quick recap
thisis chosen by how a function is called, not where it's defined:new,obj.method(),call/apply/bind, or a plainfn()- A plain call gets
undefined(strict mode), which is whyconst fn = obj.method; fn()loses the object - Arrow functions have no own
this, they inherit it lexically from where they're written - An arrow inside a method uses that method's
this; an arrow used as a method does not point at the object - React class components needed
.bind(this)or class-field arrows for this reason; hooks sidestep it
The answer that lands isn't "this is the object." It's "this depends on the call site, unless it's an arrow, in which case it depends on where the code was written."




Comments
No comments yet — be the first to share your thoughts.