Arrow functions and lexical this: when to use them, and when not to
Arrow functions take this from the code around them. That fixes callbacks inside methods, breaks object methods, and costs memory as class fields.
Comes up in: JavaScript, Recruiter screen
Learn#
Jonas writes a timer object. Inside its start method he calls setTimeout(function () { this.seconds++ }). The seconds stay at 0. In the callback, this is no longer the timer.
An arrow function fixes this. Arrows do not get their own this. They use the this of the code they are written in — lexical this, found like any variable in the scope chain. The same is true for arguments, super and new.target.
const timer = {
seconds: 0,
startBroken() {
setTimeout(function () { console.log("broken:", this === timer); }, 0);
},
startFixed() {
setTimeout(() => { this.seconds++; console.log("fixed:", this === timer, this.seconds); }, 0);
},
};
timer.startBroken();
timer.startFixed();Output (verified)
broken: false fixed: true 1
Because they have no own this, arrows cannot be used with new, and an arrow used as a method does not get the object: { go: () => this } returns the outer this.
In a DOM listener, a normal function gets the element as this; an arrow keeps the outer this. Use event.currentTarget in both cases to get the element without guessing.
startFixed's this
- timer
callback's this
- —
Console
empty
timer.startFixed() is a method call, so inside startFixed, this = timer.
Read the steps as text
- timer.startFixed() is a method call, so inside startFixed, this = timer.
- The arrow callback is created here. It has no this of its own; it will use startFixed's.
- Later, the timer calls the arrow plainly. That does not matter: an arrow ignores how it is called.
- The normal-function callback in startBroken got its this from the timer call instead — not timer.
Predict#
Predict first
const shop = { name: 'Kirana', hello: () => 'Hi from ' + this?.name }; In an ES module, what does shop.hello() return?
The arrow takes this from where it is written: the top of the module, where this is undefined. So this?.name is undefined. Calling it as shop.hello() does not help — arrows ignore the receiver. Use a normal method for object methods.
Interview angle#
Say it like this — about 30 seconds
“An arrow function has no
thisof its own. It uses thethisof the scope it is written in, and how it is called does not change that — not evencallorbind. That makes arrows ideal for callbacks inside methods. But do not use them as object or prototype methods, since they will not see the object, and they cannot be constructors. In a DOM listener, a normal function gets the element asthis; with an arrow, useevent.currentTarget.”
Follow-up When should you not use an arrow function?
arguments, or as a DOM handler that relies on this being the element.Follow-up What does this refer to inside an event listener?
event.currentTarget). For an arrow, whatever this was where the arrow was written.Follow-up Can bind change an arrow's this?
call, apply and bind can pass arguments to an arrow, but its this stays lexical.Practice#
faded
Fix the stopwatch’s three lost-this bugs. Hint: one is an arrow used as a method, two are normal functions used as callbacks.
Done when
sw.tick()and a detachedconst t = sw.tick; t()both add 1 toelapsed.sw.lapAll(3)records three laps.sw.later(cb)callscbwith the currentelapsed.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
Where does an arrow function get its this?
From the scope it is written in (lexical this).
Can you call an arrow function with new?
No — it throws a TypeError; arrows are not constructors.
Why is an arrow a bad object method?
It does not receive the object as this.
How do you get the element in any event listener?
event.currentTarget.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Normal functions: this comes from the call. Arrows: this comes from where they are written. Callback inside a method — arrow. The method itself — normal function.
Check yourself: Does obj.arrow() set this to obj?
No. Arrows ignore the receiver.
How it works underneathSenior rounds
Arrow functions have [[ThisMode]] lexical, so calling one never binds this; lookup goes to the outer environment instead Source: ECMA-262: ECMAScript function objects. They also have no [[Construct]], which is why new throws Source: MDN: Arrow function expressions.
Check yourself: What is the [[ThisMode]] of an arrow function?
lexical.
Edge cases and trapsWhere people slip
A method is stored once on the prototype and shared. A class field arrow, handle = () => {}, is created again for every instance:
class Row { save() {} handle = () => {}; }
const a = new Row(), b = new Row();
console.log(a.save === b.save, a.handle === b.handle);Output (verified)
true false
For a few components that is fine; for 10,000 rows it is 10,000 functions.
Check yourself: Do two instances share an arrow class field function?
No. Each instance gets its own copy.
In productionStaff-level answers
Arrow class fields live on the instance, so subclasses cannot call them with super, and they are harder to mock on the prototype in tests. In React function components this problem disappears: handlers are closures, not methods.
Check yourself: Why might an arrow class field break a subclass calling super.handle()?
The field is on the instance, not the prototype, so super cannot find it.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-10-31. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- How do arrow functions handle
this? When should you not use an arrow function? - What does
thisrefer to inside an event listener?