Arrow functions
Learn JavaScript arrow function syntax, implicit returns, lexical this, arguments, and when a regular function is the better tool.
- 01Read arrow syntaxTurn a small function expression into an arrow without changing what it gives back.
- 02Give back a value without returnKnow when curly braces need
return, and why an object needs round brackets around it. - 03Choose the right kind of functionExplain why arrows have no
this,arguments, orprototypeof their own, and why you can't call them withnew.
A shorter function shape
An arrow function is a short way to write a function. It looks like this: (input) => result. The => sign is the "arrow". An arrow function is still a function, so you can:
- store it in a variable,
- pass it to another function,
- call it with round brackets, like
double(6).
Programs often pass small functions to other functions. A function you pass to another function, so that it can call it later, is called a callback. For example, an array has a map method. It calls your callback once for each item and collects the results in a new array. Small callbacks like these are where arrows are used most.
In the Declaring & calling functions lesson, you wrote functions the long way, with the word function. An arrow is the short way. It works best for small jobs.
A recipe card for a cake has a title, a list of ingredients, and careful steps. A sticky note on your desk just says "double each score." Both tell you what to do. The sticky note is faster to read, because the job is small.
- In real life: A recipe card with a title and numbered steps
- In JavaScript: A function written the long way, with the word
function - In real life: A sticky note that says “toast bread, add jam”
- In JavaScript: A tiny arrow function
- In real life: The note is quick to read because the job is small
- In JavaScript: Arrows suit small callbacks and one-line helpers
An arrow function is a short way to write a function expression: a function you store as a value, for example in a variable. Arrows also have one big difference. A function can use the word this to mean "the object I was called on". An arrow doesn't get its own this. It uses the this from the place where it was written. People call this lexical this. "Lexical" means "decided by where the code is written".
In this lesson, a regular function means any function that is not an arrow. The lesson covers four things, in this order:
- How to write an arrow.
- How an arrow can give back a value without the word
return. - What arrows don't have: their own
this, their ownarguments, and the power to be called withnew. - When to use a regular function instead.
Later lessons go deeper into these ideas: The this keyword, Methods & this, Arrow functions & lexical this, and Constructors & new. This lesson gives you only the basics.
How to write an arrow
STEP THROUGHStart with a function expression: it makes a function and stores it in a variable. An arrow does the same job with fewer words. The code below writes the same helper four ways. Each one doubles a number.
const doubleA = function (number) { return number * 2;}; const doubleB = (number) => { return number * 2;}; const doubleC = (number) => number * 2;const doubleD = number => number * 2; console.log(doubleA(6), doubleB(6), doubleC(6), doubleD(6));In plain words, line by line:
- Lines 1 to 3 are the long way: the word
function, the parameternumberin round brackets, and a body in curly braces withreturn. - Lines 5 to 7 are an arrow with a body. The word
functionis gone. The=>sits between the parameter and the body. - Line 9 is an arrow with no curly braces and no
return. It gives backnumber * 2by itself. - Line 10 is the same, without the round brackets around
number. - Line 12 calls all four with 6. Each one gives back 12, so it prints
12 12 12 12.
6, and it gives back 12.The parameters go on the left of =>. The work goes on the right. The round brackets around the parameters follow one rule:
- No parameters: write empty brackets,
(). - Exactly one simple parameter: the brackets are optional.
- Two or more parameters: you need the brackets.
Pick a version to shrink the same helper. Guess the output before you step: the shape changes, but the value stays the same.
script
return number * 2;}; console.log(double(6));Pick a version above, then press Step. The code changes, but the result is always 12.
Shorter code is not always clearer. The Code style lesson said this too. Pick the shape that is easiest to read out loud.
Parameters: zero, one, or many
You need round brackets around the parameters unless there is exactly one simple parameter. A simple parameter is just a name, like name.
const ready = () => "tea";const greet = name => "Hello, " + name;const pair = (first, second) => first + " & " + second; console.log(ready());console.log(greet("Asha"));console.log(pair("Asha", "Lin"));In plain words, line by line:
- Line 1 has no parameters, so it uses empty brackets,
(). Line 5 calls it and printstea. - Line 2 has one simple parameter,
name, so it can skip the brackets. Line 6 printsHello, Asha. - Line 3 has two parameters,
firstandsecond, so they must be inside brackets. Line 7 printsAsha & Lin.
You may keep the brackets around one parameter if that is easier to read. Leaving them out is only a style choice. It does not make a different kind of function. You also need the brackets in three more cases:
- A default value, like
(name = "friend"). It is used when the call passes nothing. - A rest parameter, like
(...items). It collects all the values passed in into one array. - Destructuring, like
({ name, score }). It takes properties out of an object straight into parameters.
const welcome = (name = "friend") => "Hello, " + name;const scoreLine = ({ name, score }) => name + " has " + score + " points"; console.log(welcome());console.log(scoreLine({ name: "Asha", score: 3 }));Line 1 gives name a default value, "friend", so name = "friend" must be inside brackets. Line 4 calls it with nothing, so it prints Hello, friend.
Line 2 takes the name and score properties out of one object. Those curly braces must be inside round brackets too. Line 5 passes in the object and prints Asha has 3 points.
Giving back a value without return
INTERACTIVEWhen there are no curly braces after =>, the arrow's body is one expression. An expression is a piece of code that makes a value, like price * 0.2. The arrow gives back that value by itself, so you don't write return. This is called an implicit return. "Implicit" means it happens without being written.
A vending machine has one job per button. You press it, and the can drops out straight away. An arrow like price => price * 0.2 works the same way: you call it, and the value of price * 0.2 comes straight back. A bigger job is like fixing the machine: you open the service door and work through several steps.
- In real life: Press one button
- In JavaScript: Call an arrow that has one expression
- In real life: The can drops out straight away
- In JavaScript: The arrow gives back the value of that expression
- In real life: Open the service door to fix something inside
- In JavaScript: Use curly braces when the work needs several steps
The most common beginner mistake is giving back an object. After =>, a curly brace { starts a function body, as in the Statements lesson. It does not start an object. To give back an object without return, put round brackets around it: () => ({ a: 1 }).
return, so the arrow gives back undefined.Round brackets decide whether curly braces mean “an object” or “a function body”. Step through both calls.
script
const brokenLabel = name => { name: name }; console.log(makeLabel("Ada").name);console.log(brokenLabel("Ada"));Press Step to watch both calls. makeLabel gives back an object, so line 4 reads .name and prints Ada. brokenLabel has a body with no return, so line 5 prints undefined.
A statement is one instruction, like a line that ends with a semicolon. If the body has more than one statement, use curly braces and write return yourself: { const total = price + tax; return total; }. Don't squeeze a job with several steps into one clever line.
const salePrice = price => price - 2;const priceWithTax = price => { const tax = 1; return price + tax;}; console.log(salePrice(10));console.log(priceWithTax(10));Line 1 has one calculation, so it needs no braces. Line 7 prints salePrice(10), which is 8. The second arrow, on lines 2 to 5, first makes a variable called tax. That is an extra step, so it needs curly braces, and line 4 must say return. Line 8 prints priceWithTax(10), which is 11.
Leave out the braces when the arrow has one clear expression. Don't leave them out just to make the code shorter.
Arrows don't get their own this
INTERACTIVEA method is a function stored in an object. Inside a method, the word this usually means "the object the method was called on". A regular function gets its this from the call. In user.regular(), this is user, the object before the dot.
An arrow does not get a this from the call. It uses the this from the place where it was written.
Think of this as an address on a note. A regular method fills in the address when you call it: user.regular() gives it the address of user. An arrow's note already has an address on it, from the room where it was written. Calling it as user.arrow() does not change that address.
- In real life: A note filled in at the door you knock on
- In JavaScript: A regular method gets
thisfrom how you call it - In real life: A note that already has the address of the room where it was written
- In JavaScript: An arrow uses the
thisfrom where it was written - In real life: Carry that note to another door, and it still names the old room
- In JavaScript:
user.arrow()does not makethistheuserobject
user object. The regular method gets this from the call, so it is user. The arrow keeps the this from outside the object, which is undefined here."use strict";function makeUser() { return { name: "Ada", regular() { return this.name; }, arrow: () => this.name, };} const user = makeUser();console.log(user.regular());console.log(user.arrow());Result
The regular method is called as user.regular(), so this is the user object. It gives back Ada.
this, don't write it as an arrow.The code above is in strict mode: line 1 says "use strict". The arrow is written inside the function makeUser, and there this is undefined. So the arrow's this is undefined too, and reading this.name throws an error.
An arrow written at the top of a file has the same problem:
- In a module, a file that uses
importorexport, the top-levelthisisundefined. - In an old-style browser script, the top-level
thiscan be the global object,window.
Either way, this is not the object you wanted.
But arrows are very useful inside regular methods. A callback written as an arrow keeps the method's this. Step through the example below to see it.
This is a good use for an arrow: a short callback that should keep the this of the method around it.
script
name: "Debuggers", members: ["Ada", "Lin"], labels() { return this.members.map(member => this.name + ": " + member); },}; console.log(team.labels().join(" | "));Line 4 is a regular method. Line 9 calls it as team.labels(), so inside it this is team. On line 5, the arrow passed to map uses that same this. So this.name is Debuggers for both members, and line 9 prints Debuggers: Ada | Debuggers: Lin.
const displayName = user => user.name; console.log(displayName({ name: "Asha" }));Line 1 makes an arrow with one parameter, user. It gives back user.name. Line 3 passes in an object, so it prints Asha. No this is used. So the rule is not "arrows can't use objects". The rule is: "an arrow does not get a this from the call."
What arrows don't have: arguments, prototype, and new
COMPAREArrows are smaller than regular functions on purpose. Inside a regular function, the word arguments holds a list of all the values the call passed in. An arrow does not get its own arguments. If an arrow uses the word arguments, JavaScript looks for it outside the arrow.
At the top level of a module there is no outer arguments. So you would get a ReferenceError, an error that means "this name doesn't exist".
function outer(first, second) { const arrow = () => arguments[0] + " / " + arguments.length; return arrow("inner");} console.log(outer("outer", "second"));In plain words, line by line:
- Line 1 makes a regular function,
outer, with two parameters. - Line 2 makes an arrow inside it. The arrow reads
arguments[0], the first value, andarguments.length, how many values there are. - Line 3 calls the arrow with
"inner". The arrow has noargumentsof its own, so it reads the ones fromouter. - Line 6 calls
outerwith"outer"and"second", so it printsouter / 2.
When you want all the values in a list, use a rest parameter instead: const collect = (...items) => items.length;. The three dots collect every value into a real array. Rest parameters work in both regular functions and arrows.
const countItems = (...items) => items.length; console.log(countItems("tea", "book", "pen"));Line 1 collects every value passed in into an array called items. Line 3 calls the arrow with three items, so it prints 3. Unlike arguments, the rest parameter has a name you chose, and it belongs to this arrow.
const User = name => ({ name });console.log(User("Ada").name);console.log(new User("Ada"));A constructor is a function you call with new to build a new object. Line 2 calls User the normal way, so it prints Ada. Line 3 calls it with new, and that throws an error, because an arrow can't be a constructor.
An arrow also has no prototype property. That is the property a constructor uses to share methods with the objects it builds. In Chrome and Node, the error reads TypeError: User is not a constructor. Other browsers may word it differently, so remember the rule, not the exact message.
function outer(first, second) { const arrow = () => arguments[0] + " / " + arguments.length; return arrow("inner");} console.log(outer("outer", "second"));The arrow reads arguments from outer. Calling the arrow with "inner" does not give it its own arguments.
Use the three buttons to switch between the examples. Each one shows the output the code really gives.
When to use a regular function instead
Arrows are a tool. You don't have to use them everywhere. Use a regular function or method when the function needs any of these:
- its own
this, from the call, - a clear name that explains a long job,
- to be called with
new.
The table below compares the two kinds. Read each row as: "for this question, here is the regular function, and here is the arrow."
| What you want to know | Regular function | Arrow function |
|---|---|---|
| What does it look like? | function add(a, b) { return a + b; } | (a, b) => a + b |
Where does this come from? | From how you call it. In user.regular(), this is user. | From the place where the arrow was written. |
Does it have its own arguments? | Yes. arguments holds every value the call passed in. | No. Use a rest parameter, like (...items), instead. |
Can you call it with new? | Yes, if you wrote it to build objects. | No. It throws a TypeError, and it has no prototype property. |
| Can you call it before its line? | Yes, if it is a function declaration. JavaScript sets those up before the code runs. | No. It is usually stored in a const, and you can't use that variable before its line. |
| What is it best for? | Methods, constructors, and named jobs you use again and again. | Short callbacks and tiny helpers. |
An array callback: `scores.map(score => score * 2)`An object method that reads `this.name`- A function you plan to call with
newIt belongs in Better as a regular function. You can't call an arrow with
new, and an arrow has noprototypeproperty. A one-line helper: `const tax = price => price * 0.1`- A callback inside a method that should keep the method's
this Code that needs its own `arguments` object
Put each case in a group: a good fit for an arrow, or better as a regular function. If an answer surprises you, read its explanation before you go on.
An event handler is a function the browser calls when something happens, like a click. In the Events lessons, some handlers use this to mean the element that was clicked. Those handlers must be regular functions. Other handlers use event.currentTarget to find the element. Those can be arrows.
Where you'll use arrow functions
In everyday code, you'll see arrows most in two places: callbacks for array methods, and tiny helpers. The arrow keeps the small job right next to the data it works on. These array methods take a callback:
mapcalls it once for each item and collects what it gives back into a new array.filterkeeps only the items for which it gives backtrue.findgives back the first item for which it gives backtrue.
You'll also see join. It glues the items of an array into one string, with the text you give between them.
const prices = [5, 12, 20];const withTax = prices.map(price => price * 1.1);const affordable = withTax.filter(price => price < 15); const cart = { label: "Cart", items: ["book", "pen"], lines() { return this.items.map(item => this.label + ": " + item); },}; console.log(affordable.join(", "));console.log(cart.lines().join(" | "));In plain words, line by line:
- Line 2:
mapruns the arrow on each price and adds 10% tax. - Line 3:
filterkeeps only the prices under 15. - Lines 8 and 9:
lines()is a regular method, because it needsthisto becart. The arrow inside it uses that samethis. - Line 13 prints the prices that are left. Line 14 prints
Cart: book | Cart: pen.
const cart = [ { name: "tea", price: 3 }, { name: "book", price: 8 },]; const labels = cart.map(item => item.name + ": $" + item.price); console.log(labels.join(" | "));Lines 1 to 4 make an array of two item objects. On line 6, map passes one item at a time to the arrow. The arrow gives back a label for each item, like tea: $3. Line 8 joins the labels and prints tea: $3 | book: $8.
This callback is a good fit for an arrow. It doesn't use this, it doesn't change the cart, and it has only one step. If the label rules grew long, you would give the function a clear name, like formatCartItem, instead of squeezing it into one line.
const todos = [ { name: "tea", done: false }, { name: "book", done: true },]; const openTodos = todos.filter(todo => !todo.done);const finishedTodo = todos.find(todo => todo.done); console.log(openTodos[0].name);console.log(finishedTodo.name);Both arrows answer a yes-or-no question about one todo. On line 6, filter keeps every todo that is not done (! means "not"). So line 9 prints the first open todo's name, tea. On line 7, find stops at the first todo that is done, so line 10 prints book. Neither method changes the original todos array.
const formatItem = item => item.name + ": $" + item.price;const cart = [{ name: "tea", price: 3 }, { name: "book", price: 8 }]; console.log(cart.map(formatItem).join(" | "));Line 1 stores the arrow in a variable called formatItem. Line 4 passes it to map by name, without round brackets, so map can call it for each item. It prints tea: $3 | book: $8.
The name tells the reader what map is doing. A named helper is also easier to test, use again, and grow than a long callback.
Common mix-ups
"Arrows are just shorter functions."
They are shorter, but they also work differently. Arrows have no this, arguments, or prototype of their own, and you can't call them with new.
"An arrow with curly braces gives back its last line."
No. Curly braces make a function body. Without return, the arrow gives back undefined.
"Arrows are always the better style."
No. They are great for short callbacks. Methods that use this, constructors, and long named jobs are usually clearer as regular functions.
"For an arrow, the object before the dot becomes this."
No. That rule is for regular functions. An arrow ignores it and uses the this from where it was written.
"Use arguments in an arrow to get extra values."
No. Arrows have no arguments of their own. Use a rest parameter instead: (...values) => values.length.
The code below shows the second mix-up. Line 1 has round brackets around the object, so line 4 prints Ada. Line 2 has curly braces and no return, so line 5 prints undefined.
const makeLabel = name => ({ name, active: true });const brokenLabel = name => { name: name }; console.log(makeLabel("Ada").name);console.log(brokenLabel("Ada"));Practice: choose and fix arrows
6 EXERCISESThe exercises start easy and get harder. Make a guess first. Use the hints if you get stuck.
Rewrite the function as an arrow. The output must stay the same. What does your version print?
const add = function (a, b) {
return a + b;
};
console.log(add(2, 3));const add = (a, b) => a + b;
console.log(add(2, 3));The arrow has the same two parameters and gives back the same expression, so it prints 5.
This arrow should give back an object. Fix it, then type the value it prints.
const makePoint = (x, y) => { x, y };
console.log(makePoint(2, 4).y);const makePoint = (x, y) => ({ x, y });
console.log(makePoint(2, 4).y);The round brackets make the curly braces an object. The object has y: 4, so it prints 4.
The arrow method on the last line throws an error. Before that, what does the regular method give back?
"use strict";
function makeUser() {
return {
name: "Ada",
regular() { return this.name; },
arrow: () => this.name,
};
}
const user = makeUser();
console.log(user.regular());
console.log(user.arrow());user.regular() gives back Ada. The next line, user.arrow(), throws. The code is in strict mode, and the arrow uses the this from makeUser, which is undefined.
You are writing an object method that uses this.name. Type the safer choice: arrow or regular.
Use a regular method. Arrows are good for the small callbacks inside that method, but not for the method itself when it needs this.
Run the code. What exact text does it print?
const team = {
name: "Debuggers",
members: ["Ada", "Lin"],
labels() {
return this.members.map(member => this.name + ": " + member);
},
};
console.log(team.labels().join(" | "));const team = {
name: "Debuggers",
members: ["Ada", "Lin"],
labels() {
return this.members.map(member => this.name + ": " + member);
},
};
console.log(team.labels().join(" | "));team.labels() gives the regular method team as this. The arrow callback uses that same this, so both labels start with Debuggers.
A small shop page needs a text summary of its cart. One arrow callback turns the item objects into names. What exact line does the page print?
const cart = [{ name: "tea" }, { name: "book" }];
const names = cart.map(item => item.name);
console.log(names.join(", "));const cart = [{ name: "tea" }, { name: "book" }];
const names = cart.map(item => item.name);
console.log(names.join(", "));The arrow gets each cart item and gives back just its name. map builds ["tea", "book"], and join turns that into tea, book. A real shop page could use the same code to list its products.
Quiz: check your understanding
7 QUESTIONSFor each question, first look at what comes after =>: curly braces, or an expression. For questions about this, ask whether the function is an arrow or a regular function. Each answer explains why it is right or wrong.
Question 1 of 7Which arrow does the same job as
function (x) { return x * 2; }?Choose an answer to see the explanation.
Question 2 of 7What does this arrow, which gives back an object, print?
Read the code, then predictconst make = () => ({ a: 1 }); console.log(make().a);Choose an answer to see the explanation.
Question 3 of 7What does this arrow with curly braces print?
Read the code, then predictconst make = () => { a: 1 }; console.log(make());Choose an answer to see the explanation.
Question 4 of 7Why should an object method that uses
this.nameusually not be an arrow?Choose an answer to see the explanation.
Question 5 of 7What does this code, which uses
argumentsinside an arrow, print?Read the code, then predictfunction outer(first, second) { const arrow = () => arguments[0] + " / " + arguments.length; return arrow("inner"); } console.log(outer("outer", "second"));Choose an answer to see the explanation.
Question 6 of 7Which sentence about
new (() => {})is true?Choose an answer to see the explanation.
Question 7 of 7Which callback is best in
numbers.map(_____)to multiply each number by 10?Choose an answer to see the explanation.
Key takeaways
- An arrow function is a short way to write a function expression:
(value) => result. - Without curly braces, an arrow gives back its expression by itself. With curly braces, you must write
return. - To give back an object, put round brackets around it:
() => ({ a: 1 }). - Arrows have no
this,arguments, orprototypeof their own, and you can't call them withnew. - Use arrows for short callbacks. Use regular functions for methods that need
this, for constructors, and for code that needsarguments.
Remember the one-liner.
An arrow function is a short function. It can give back a small result without return, and it uses the this from where it was written.
Up next: Recursion, where a function calls itself.