Arrow Functions
After completing this topic, you will be able to:
- Use arrow function syntax fluently.
- Explain the key difference between arrow functions and regular functions (this binding).
- Determine when to use arrow functions and when to avoid them.
From function keyword to arrow function
Before ES6, we always used the function keyword to create functions:
const add = function(a, b) {
return a + b;
};With arrow functions:
const add = (a, b) => {
return a + b;
};If the body contains only a single expression, you can omit the curly braces and the return keyword:
const add = (a, b) => a + b;If there is only one parameter, you can also omit the parentheses:
const double = x => x * 2;Summary of syntax variations
// No parameters — parentheses required
const greet = () => "Hello";
// One parameter — parentheses can be omitted
const square = x => x * x;
// Two or more parameters — parentheses required
const sum = (a, b) => a + b;
// Multi-line body — curly braces and return required
const calculate = (a, b) => {
const result = a * b;
console.log(result);
return result;
};
// Returning an object — wrap in parentheses
const makeUser = (name) => ({ name: name, role: "user" });The last pattern is often confusing. If you just write { name: name }, JavaScript will interpret it as the function body's curly braces. You need to wrap it in parentheses () to tell JavaScript, "This is an object literal."
Arrow functions shine in callbacks
Arrow functions are most natural when used as callbacks in array methods:
const numbers = [1, 2, 3, 4, 5];
// Regular function
const doubled = numbers.map(function(n) {
return n * 2;
});
// Arrow function
const doubled = numbers.map(n => n * 2);Using function every time in array methods like filter, reduce, forEach, and sort makes the code longer and obscures the core logic. This is one of the main reasons arrow functions were adopted.
// Real-world pattern: chaining
const result = users
.filter(u => u.age >= 18)
.map(u => u.name)
.sort((a, b) => a.localeCompare(b));this — the most important difference
In regular functions, this is determined by the calling context. In arrow functions, this is determined by the lexical scope where it was defined:
const timer = {
seconds: 0,
// ❌ Regular function — this will refer to window
startBroken: function() {
setInterval(function() {
this.seconds++; // this === window (or undefined in strict mode)
console.log(this.seconds); // NaN
}, 1000);
},
// ✅ Arrow function — this will refer to the timer object
start: function() {
setInterval(() => {
this.seconds++; // this === timer
console.log(this.seconds); // 1, 2, 3...
}, 1000);
}
};When you pass a regular function to setInterval, the this inside that function will be the global object (window) when it executes. This is one of the biggest pitfalls in JavaScript, where the this in the callback is different from what you expect.
Arrow functions do not create their own this. Instead, they inherit the this from the surrounding scope where they were defined. This is called lexical this.
Before ES6, you had to use workarounds like var self = this; or .bind(this) to avoid this problem:
// Workaround in ES5
start: function() {
var self = this;
setInterval(function() {
self.seconds++;
}, 1000);
}Arrow functions completely replace this pattern.
When not to use arrow functions
1. Object methods
const user = {
name: "철수",
// ❌ Arrow — this will not refer to the user object
greet: () => {
console.log(`Hello, ${this.name}`); // undefined
},
// ✅ Regular function shorthand — this will refer to the user object
greet() {
console.log(`Hello, ${this.name}`); // "Hello, 철수"
}
};When you use an arrow function as a method in an object literal, this will not refer to the object, but to the outer scope (usually the global scope).
2. Constructors
// ❌ Cannot use arrow functions as constructors
const Person = (name) => { this.name = name; };
new Person("철수"); // TypeError: Person is not a constructorArrow functions do not have a prototype, so you cannot use the new keyword with them.
3. DOM event handlers where you need this
// ❌ Arrow — this will not refer to the button
button.addEventListener("click", () => {
this.classList.toggle("active"); // this !== button
});
// ✅ Regular function — this will refer to the event target element
button.addEventListener("click", function() {
this.classList.toggle("active"); // this === button
});However, you can often use event.target or event.currentTarget instead, so there are cases where you can still use arrow functions.
What arrow functions lack
| Feature | Regular Function | Arrow Function |
|---|---|---|
Own this | ✅ | ❌ (lexical) |
arguments object | ✅ | ❌ |
Can be called with new (constructor) | ✅ | ❌ |
prototype | ✅ | ❌ |
super | ✅ | ❌ (lexical) |
Instead of arguments, use rest parameters (...args):
// Regular function
function sum() {
return [...arguments].reduce((a, b) => a + b, 0);
}
// Arrow function
const sum = (...args) => args.reduce((a, b) => a + b, 0);Using ...args is more explicit than arguments and allows you to use array methods directly, so the arrow function approach is often preferred.
Decision criteria
Is it a callback function? → Use an arrow function
Is it an array method (map/filter/...) ? → Use an arrow function
Is it an object's method? → Use a regular function (shorthand syntax)
Do you need a constructor? → Use a regular function (or class)
Do you need to dynamically change `this`? → Use a regular functionKey takeaway: Use arrow functions for
() => {}, and they will use thethisfrom the surrounding scope (lexicalthis). Use arrow functions for callbacks, and regular functions for object methods — just remember this.