Arrow functions are a shorter way to write function expressions. You will see them everywhere in modern JavaScript, especially as callbacks. They are not just shorter, though: they behave differently in a few ways that matter. This lesson covers the syntax and when an arrow is the wrong choice.
From function expression to arrow
Here is a function expression from the last lesson:
const square = function (n) {
return n * n;
};
The same function as an arrow function:
const square = (n) => {
return n * n;
};
console.log(square(4));
// 16
Drop the function keyword and put => (an "arrow") between the parameters and the body. Everything else works the same way: parameters, default values and return.
Implicit return
When the body is a single expression, you can leave out the braces and the return keyword. The arrow returns the value of the expression automatically:
const square = (n) => n * n;
console.log(square(5));
// 25
This is called an implicit return. It makes small functions very compact:
const add = (a, b) => a + b;
const isEven = (n) => n % 2 === 0;
const greet = (name = "friend") => `Hello, ${name}!`;
console.log(add(2, 3));
// 5
console.log(isEven(7));
// false
console.log(greet());
// Hello, friend!
The rule is simple: with braces, you need return; without braces, the expression is returned. Mixing them up is a common mistake:
const double = (n) => {
n * 2;
};
console.log(double(4));
// undefined
The braces make a block body, so the calculation happens and is thrown away. Either remove the braces or add return.
Parameter shapes
The brackets around parameters are optional when there is exactly one parameter, and required otherwise:
const shout = text => text.toUpperCase(); // one parameter, brackets optional
const now = () => Date.now(); // no parameters, brackets required
const area = (width, height) => width * height; // two parameters, brackets required
Many teams always include the brackets, so adding a second parameter later does not mean rewriting the function, and code formatters such as Prettier add them by default. This course always includes them.
Returning an object
To return an object literal implicitly, wrap it in brackets. Otherwise the braces are read as a function body:
const makeUser = (name) => { name: name };
console.log(makeUser("Ada"));
// undefined
const makeUserFixed = (name) => ({ name: name });
console.log(makeUserFixed("Ada"));
// { name: "Ada" }
In the first version, JavaScript sees a block containing a label called name, which is valid but useless, and returns nothing. The brackets in the second version make it an expression. Lesson 13 covers objects.
Arrows as callbacks
Arrow functions shine when you pass a short function to another function:
const prices = [4, 10, 7];
const withVat = prices.map((price) => price * 1.2);
console.log(withVat);
// [4.8, 12, 8.4]
map() calls the arrow once for each item and collects the results into a new array. You will learn map() and its relatives in lesson 22. Compare the arrow with the older style:
const withVatOld = prices.map(function (price) {
return price * 1.2;
});
Both work. The arrow version puts the important part, price * 1.2, front and centre.
You saw an arrow callback in lesson 1 too, passed to addEventListener:
button.addEventListener("click", () => {
button.textContent = "Thanks!";
});
How arrows differ from regular functions
Arrow functions are not just shorthand. They leave out several features that regular functions have.
No own this
Regular functions get a value called this that depends on how they are called. Arrow functions do not have their own this; they use the this of the code around them. That makes arrows good for callbacks inside methods, and a poor choice for methods themselves:
const counter = {
count: 0,
increment() {
this.count++;
return this.count;
},
broken: () => {
return this;
},
};
console.log(counter.increment());
// 1
increment is a regular method, so this is counter. broken is an arrow, so this is whatever it was outside the object, never counter. Lesson 21 explains this fully. For now, use the method shorthand name() { ... } for object methods, not arrows.
Cannot be used with new
Regular functions can be called with new to create objects. Arrows cannot:
const Person = (name) => {
this.name = name;
};
new Person("Ada");
// Uncaught TypeError: Person is not a constructor
Use a class for this, as lesson 36 shows.
No arguments object
Regular functions have a special arguments variable holding every argument passed. Arrows do not. Use rest parameters, (...args) => {}, instead; lesson 23 covers them.
Not hoisted
An arrow function is always an expression, usually stored in a const. Like any function expression, it cannot be called before the line that creates it.
When not to use an arrow
| Situation | Use |
|---|---|
| Short callbacks for array methods, timers and promises | Arrow function |
Event listener that does not need this | Arrow function |
Object methods that use this | Method shorthand: name() {} |
Creating objects with new | A class |
| Top-level named functions in a file | Either; many teams prefer declarations for these |
An event listener that uses this to mean the element | A regular function, or better, event.currentTarget |
Arrow functions are a good default for small functions you pass around. When a function needs its own this, reach for a regular function or method.
Readability
Arrow functions can be written so compactly that they become hard to read. A few habits help:
- Use an implicit return only when the expression fits comfortably on a line.
- Give callbacks meaningful parameter names:
(price) => price * 1.2, not(p) => p * 1.2, unless the meaning is obvious. - If a callback grows beyond a few lines, consider moving it into a named function and passing the name.
const applyVat = (price) => price * 1.2;
const withVat = [4, 10, 7].map(applyVat);
console.log(withVat);
// [4.8, 12, 8.4]
Notice that you pass applyVat without brackets. You are handing over the function itself for map() to call, not calling it yourself.
Summary
- Arrow functions use
(params) => bodyand are a shorter form of function expression. - A body without braces returns its expression automatically. With braces, you need
return. - Wrap an object literal in brackets to return it implicitly:
() => ({ ... }). - Arrows are ideal for short callbacks.
- Arrows have no own
this, noarguments, cannot be used withnewand are not hoisted. - Use method shorthand, not arrows, for object methods that use
this.