Lesson 12·M2 Data & Functions
Arrow Functions
OpenLearning Objectives
By the end of this lesson you should be able to:
- Explain the reason arrows exist, not just the syntax
- Trace which
thisan arrow sees, in any nesting - Say exactly how arrows differ from regular functions beyond the shorthand
- Use them in React callbacks without losing state or
this - Say when an arrow is the wrong tool — methods, constructors, generators
One-line definition
An arrow function has no this, arguments, super or new.target of its own — it
inherits them lexically from the scope where it's written.
Mental model
A regular function asks "how was I called?" to find this. An arrow never asks — it
looks up the surrounding code, like a variable following the scope chain from Lesson 2.
"Shorter syntax" is a side effect; the behavior is the feature.
regular: this ← how it's called (call-time, Lesson 10)
arrow: this ← where it's written (definition-time)Visual Flow
object method
└── this = the object (implicit binding)
function expression inside a method
└── this = its own, call-time → usually undefined
arrow inside a method
└── this = the method's this → the object againHow It Works
The four binding rules from Lesson 10 apply to regular functions. Arrows skip them all —
no this, arguments, super or new.target of their own:
const f = () => {};
console.log(f.hasOwnProperty('prototype')); // no prototype → not a constructor
console.log(f.length);
Output:
false
0
This is the whole reason they exist — the classic timer bug and its fix:
const timer = {
label: 'timer',
startBroken() {
setTimeout(function () {
console.log('broken:', this?.label); // function's own this
}, 0);
},
startFixed() {
setTimeout(() => {
console.log('fixed:', this.label); // inherited from startFixed
}, 0);
},
};
timer.startBroken();
timer.startFixed();
Output:
broken: undefined
fixed: timer
The same idea nested: an arrow inside an arrow keeps climbing until it finds a this:
const outer = {
label: 'outer',
method() {
const first = () => this.label; // outer
const second = () => first() + ' → ' + this.label; // still outer
return second();
},
};
console.log(outer.method());
Output:
outer → outerReal Project Usage
| Where | Why the arrow wins |
|---|---|
setTimeout, event callbacks | Inherits the surrounding this — no bind needed |
.map, .filter, .reduce | Short return, no accidental this to lose |
| React event handlers | onClick={() => …} keeps component this/state |
| Promise chains | .then(v => …) stays in the enclosing scope |
| Class field arrows (Lesson 10) | One arrow per instance, this pre-fixed |
In React, the callback keeps seeing the right this — and with hooks there is no this
at all, so arrows are just the natural shape:
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}Interview Explanation
"Arrow functions exist for lexical this. A regular function decides this at call
time, so it breaks when passed as a callback. An arrow has no this of its own — it uses
the one from the code around it, so a timer callback still sees the object it was written
in. That removes the old const self = this and bind dance. Shorter syntax is a bonus,
not the reason."
Senior-Level Insights
- An arrow inherits
thisthrough the scope chain (Lesson 2), so it also inherits across functions, blocks and modules — there is no "arrow with its ownthis". - Arrows can't be constructors. Calling one with
newis aTypeError, and they have noprototypeproperty — a reliable one-line interview check. - They also don't get
arguments. Rest parameters ((...args)) are the replacement. - Because
thisis lexical, an arrow can't be rebound withcall/apply/bind— the arguments still pass, thethisargument is ignored. - They can't be generators — no
function*arrow form exists.
Common Mistakes
// ❌ arrow as an object method — no enclosing object scope to inherit from
const bad = {
name: 'bad',
greet: () => this.name,
};
console.log(bad.greet());
Output:
undefined
The object literal doesn't create a scope, so the arrow looks further out — to the
module, where this is undefined. Fix with a method shorthand:
const good = {
name: 'good',
greet() {
return this.name;
},
};
console.log(good.greet());
Output:
good
// ❌ calling an arrow before its line — it's an expression (Lesson 11), TDZ throws
try {
greet();
} catch (e) {
console.log(e.constructor.name);
}
const greet = () => 'hi';
Output:
ReferenceErrorBest Practices
✅ Use arrows for callbacks — timers, events, array methods, promise chains
✅ Use arrows in React when you don't need the element/event as this
✅ Use the implicit return only for single expressions
✅ Reach for (...args) instead of arguments inside arrows
❌ Don't use an arrow as an object or prototype method
❌ Don't use an arrow for a constructor, generator, or when you need this from the caller
Interview Questions
Q1. Why do arrow functions exist?
For lexical
this. A regular function getsthisfrom how it's called, which breaks when it's passed as a callback. An arrow has nothisof its own and uses the surrounding scope's, so the fix is automatic. The shorter syntax is a bonus, not the point.
Q2. How is this different inside an arrow function?
There is no
thisinside an arrow — the identifier resolves through the scope chain to the enclosing function's. That's why arrows can't be rebound withcall,applyorbind, and why they don't work as constructors.
Q3. What can't an arrow function do?
Be a constructor — no
prototype, calling withnewthrows. Be a generator — there's nofunction*arrow form. Have its ownargumentsorthis. And as a method on an object or prototype it can't get the object from the call.
Q4. When would you avoid an arrow function?
Whenever the caller's
thismatters — object methods, prototype methods, DOM event handlers that wantthisto be the element, and constructors. Use a regular function or method shorthand there.
Senior follow-up: Why does () => this ignore .call(), and what does that imply?
Because the arrow never creates a
thisbinding, socall's first argument has nothing to bind to and is silently dropped — the function still receives its other arguments. Implication: you can't use arrows to build "bound" utilities, and if you need per-call context you must pass it as a parameter instead.
Follow-Up Questions
Do arrow functions get hoisted?
No — an arrow is always an expression (Lesson 11), so only its
const/letbinding is hoisted, into the Temporal Dead Zone. Call one before its line and you get aReferenceError.
Can you use arguments inside an arrow?
No, but you rarely need to — a rest parameter
(...args)is cleaner and works in arrows, andargumentsinside a regular function is already a smell.
Do arrows work with this in class fields?
Yes — a class-field arrow (Lesson 10) is created once per instance with
thisalready pointing at the instance, which is exactly why it's the standard fix for passing a method as a callback.
Comparison Table
| Property | Regular function | Arrow function |
|---|---|---|
this | Call-time (Lesson 10) | Lexical — inherited |
arguments | Own | Inherited (use ...args) |
Constructor (new) | ✅ | ❌ TypeError |
prototype property | ✅ | ❌ |
Generator (function*) | ✅ | ❌ |
call/apply/bind | Rebindable | this argument ignored |
| Object method | ✅ (method shorthand) | ❌ |
| Implicit return | ❌ | ✅ single expressions |
| Hoisting | Declaration form hoists | Never — always an expression |
Code Example
Predict the output, then run it:
'use strict';
const arrow = () => 'arrow';
console.log(arrow.hasOwnProperty('prototype'));
try {
new arrow();
} catch (e) {
console.log('new:', e.constructor.name);
}
console.log(arrow.call({ forced: true })); // this argument ignored
const collect = (...args) => args;
console.log(collect(1, 2, 3));
Output:
false
new: TypeError
arrow
[ 1, 2, 3 ]Performance Notes
Arrows are not meaningfully faster than regular functions — V8 compiles both to
optimized code. The real costs are elsewhere: an arrow created inside a render or a hot
loop allocates a fresh function each time, so hoist stable callbacks out when it shows up
in profiling. Class-field arrows trade one allocation per instance for correct this —
that's the conscious trade.
Debugging Scenarios
"this is undefined in my method" — you used an arrow as an object or prototype
method. It has no own this, and there's no enclosing function scope. Switch to method
shorthand.
"this is the wrong object in my timer" — a regular function callback is re-deciding
this at call time (Lesson 10). Use an arrow, or bind the callback.
"Cannot read properties of undefined" inside an arrow — you read this.x where
this is inherited as undefined (module top level, strict mode). The arrow is doing
exactly what it's supposed to; the fix is to check where the enclosing this comes from.
"Unexpected token '=>'" — the codebase predates ES2015 or the parser is misreading a
</> next to =>. Modern tooling doesn't hit this; if it does, it's a build config
issue, not a syntax one.
Quick Revision Notes
- Arrows exist for lexical
this— say that first, always - No
this,arguments,super,new.target,prototype; not constructible - Inherit
thisthrough the scope chain — no own binding to rebind - Never an object/prototype method; use method shorthand there
- Always an expression — TDZ, no hoisting (Lessons 11, 4)
- Implicit return for single expressions;
...argsreplacesarguments - React callbacks: arrows are the natural shape, especially with hooks
Cheat Sheet
() => expr implicit return — no braces
() => { … } block body — explicit return
x => x * 2 one parameter, no parens
(a, b) => a + b multiple parameters
async () => await f() async arrows exist
function* () {} ❌ no generator arrowKey Takeaways
Recap
- Lexical
thisis the reason arrows exist — shorter syntax is the bonus - An arrow inherits
this,argumentsandnew.targetfrom its enclosing scope - No
prototype, no constructor, no generator form — andcall/bindcan't rebind them - Wrong tools: object methods, prototype methods, anything needing call-time
this - Always an expression: TDZ, never hoisted (Lessons 11 and 4)
- In React, arrows are the default callback shape — with hooks there's no
thisto lose
Check your understanding
Answer each one out loud. Then rate yourself honestly.
0/6In one sentence: why do arrow functions exist?
Trace what this is inside an arrow nested two functions deep.
Why can't call or bind change an arrow's this?
Name three things an arrow function cannot do.
Why is an arrow the wrong tool for an object method?
What replaces arguments inside an arrow, and why is it better?
What's Next
Lesson 13 — Higher-Order Functions & Callbacks. Arrows are the perfect value to pass around — and passing functions as values is the whole idea.