JavaScript Arrow Functions Get Certified
Arrow functions, introduced in ES6, offer a shorter syntax for writing function expressions. They are especially popular for short callbacks, but they also behave differently from regular functions in a few important ways.
In this lesson, you will learn the arrow function syntax, how
implicit returns work, and how arrow functions handle
this differently from regular functions.
Basic Arrow Function Syntax
An arrow function replaces the function keyword with
an arrow (=>) placed after the parameter list.
What this code does: An arrow function is a shorter way to write a function expression.
sayHi is defined with => and no parameters. Calling it prints the greeting.
const sayHi = () => {
console.log("Hi there!");
};
sayHi();
const sayHi = () => { creates an arrow function and stores it in the constant sayHi. The empty parentheses () are the parameter list (this function takes no parameters). The arrow => replaces the function keyword, and the brace { starts the function body.console.log("Hi there!"); is the code that runs each time the function is called. It prints Hi there!.}; closes the function body with } and ends the assignment with a semicolon, just as with any function expression.sayHi(); calls the function by name. It runs the body and prints Hi there!. An arrow function is called exactly like any other function.Arrow Functions With Parameters
Parameters are written inside parentheses, just like regular functions.
What this code does: Arrow functions can take parameters and use a full body with curly braces and return.
multiply multiplies its two arguments and returns the result, which is then printed.
const multiply = (a, b) => {
return a * b;
};
console.log(multiply(4, 7));
const multiply = (a, b) => { stores an arrow function in multiply. The parameters a and b go inside the parentheses, separated by a comma, before the arrow. With two parameters, the parentheses are required.return a * b; multiplies the parameters and sends the result back. Because the body is wrapped in curly braces, the return keyword is required to give a value back.}; closes the function body and ends the assignment.multiply(4, 7) passes 4 to a and 7 to b. The function returns 28, which console.log prints.Implicit Return
If the function body is a single expression, you can omit the
curly braces and the return keyword. The result of
the expression is returned automatically.
What this code does: With a single expression, you can omit curly braces and return (implicit return).
(a, b) => a * b multiplies the values and returns the product in one short line.
const multiply = (a, b) => a * b;
console.log(multiply(6, 3));
const multiply = (a, b) => a * b; is the short form. After the arrow there are no curly braces and no return keyword, just the expression a * b. When the body is a single expression, JavaScript returns its value automatically. This does exactly the same as the previous example, in one line.multiply(6, 3) returns 18, and console.log prints it.
This shorthand only works when the function body is a single
expression. If you use curly braces, you must use
return explicitly.
Parentheses With a Single Parameter
When an arrow function has exactly one parameter, the parentheses around it are optional.
What this code does: A single parameter can drop the parentheses around its name.
celsius => ... converts 20°C to Fahrenheit and prints the result.
const celsiusToFahrenheit = celsius => (celsius * 9 / 5) + 32;
console.log(celsiusToFahrenheit(20));
const celsiusToFahrenheit = celsius => (celsius * 9 / 5) + 32; has exactly one parameter, so the parentheses around celsius can be left out. After the arrow, the single expression is returned automatically. The formula multiplies by 9, divides by 5 and adds 32; the parentheses around (celsius * 9 / 5) are only for readability.celsiusToFahrenheit(20) calculates 20 * 9 / 5, which is 36, and then adds 32. The result is 68, which is printed.Parentheses are always required when there are zero parameters, or two or more parameters.
Arrow Functions With No Parameters
What this code does: An arrow function with no parameters still needs empty parentheses ().
This one returns a random integer from 0 to 99 each time it is called.
const getRandomNumber = () => Math.floor(Math.random() * 100);
console.log(getRandomNumber());
const getRandomNumber = () => Math.floor(Math.random() * 100); takes no parameters, so empty parentheses () are required before the arrow. The body is one expression, so it is returned automatically. Math.random() gives a decimal from 0 up to (but not including) 1. Multiplying by 100 makes it a decimal from 0 up to 100, and Math.floor() rounds it down to a whole number from 0 to 99.getRandomNumber() returns a new random whole number from 0 to 99 each time it is called, and console.log prints it. Run the code several times to see different numbers.Returning an Object Literal
Returning an object literal with implicit return requires wrapping it in parentheses, since curly braces would otherwise be interpreted as the function body.
What this code does: To return an object with implicit return, wrap the object in parentheses. Without them, curly braces would be treated as a function body. The returned user object is printed.
const createUser = (username, role) => ({ username: username, role: role });
console.log(createUser("michael_dev", "admin"));
const createUser = (username, role) => ({ username: username, role: role }); returns an object literal. The object is wrapped in extra parentheses ( ... ). Without them, JavaScript would read the curly braces as the function body instead of an object, and the function would return undefined. Inside the object, each property has a name and a value taken from the parameters.createUser("michael_dev", "admin") returns an object with username set to "michael_dev" and role set to "admin". console.log prints that object.Arrow Functions as Callbacks
Arrow functions are widely used as short, inline callbacks for array methods.
What this code does: Arrow functions shine as short callbacks for array methods.
map adds tax to each price; filter keeps only prices above 20. Both results are printed.
const prices = [19.99, 45.5, 8.25, 120];
const withTax = prices.map(price => price * 1.08);
console.log(withTax);
const expensiveItems = prices.filter(price => price > 20);
console.log(expensiveItems);
const prices = [19.99, 45.5, 8.25, 120]; creates an array of four prices.prices.map(price => price * 1.08) calls map() with a very short arrow function. It runs once for each price, and the returned values form a new array. Multiplying by 1.08 adds 8% tax. The single parameter needs no parentheses, and the single expression is returned automatically.console.log(withTax); prints the new array: approximately 21.59, 49.14, 8.91 and 129.6. (You may see small rounding digits such as 21.589199999999998, which is normal for decimal numbers in JavaScript.)prices.filter(price => price > 20) calls filter() with an arrow function that returns true or false. Only the prices for which it returns true are kept in the new array.console.log(expensiveItems); prints the filtered array: 45.5 and 120. The prices 19.99 and 8.25 were left out because they are not above 20.Arrow Functions Do Not Have Their Own this
One of the biggest differences between arrow functions and
regular functions is how they handle this. An arrow
function does not create its own this — instead, it
uses this from the surrounding (enclosing) scope.
What this code does: Arrow functions do not have their own this; they inherit it from the surrounding scope.
Inside start, the arrow callback can still update this.seconds on the timer object.
(The interval is commented out so it does not run forever in the sandbox.)
const timer = {
seconds: 0,
start: function () {
setInterval(() => {
this.seconds++;
console.log(this.seconds);
}, 1000);
}
};
// timer.start(); // Uncomment to see it count up in a real browser.
console.log("Arrow functions inherit 'this' from their surrounding scope.");
const timer = { creates an object named timer.seconds: 0, is a property that stores the number of seconds counted so far. It starts at 0.start: function () { defines a method called start using a regular function. When it is called as timer.start(), this inside it refers to the timer object.setInterval(() => { asks the browser to call a function repeatedly. The function is an arrow function. An arrow function has no this of its own, so it uses the this from the surrounding start method, which is timer.this.seconds++; adds 1 to timer.seconds on each tick. This works only because the arrow function inherited this from start.console.log(this.seconds); prints the current count each time: 1, 2, 3 and so on.}, 1000); closes the arrow function body with }, then 1000 is the delay in milliseconds (one second), and ); ends the setInterval call.} closes the start function.}; closes the timer object and ends the statement.// timer.start(); is a comment, so the timer is not started in this example. A running interval would keep going forever, and the output panel of the test editor only shows what is logged while the code runs. To see it count, run it in a real browser console by removing the //.Arrow functions inherit 'this' from their surrounding scope. This is the only output of the example.
If a regular function were used instead of an arrow
function inside setInterval, this would
no longer refer to timer, and
this.seconds would not work as expected.
When Not to Use Arrow Functions
Because arrow functions don’t have their own this,
they generally shouldn’t be used as object methods that rely on
this to refer to the object itself.
What this code does: Using an arrow function as an object method is usually wrong when you need this.
Here this is not the car object, so this.brand is undefined. Prefer a regular function for methods.
const car = {
brand: "Toyota",
describe: () => {
console.log(this);
return "Brand: " + this.brand;
}
};
console.log(car.describe());
const car = { creates an object named car.brand: "Toyota", stores the car’s brand.describe: () => { defines the describe method as an arrow function. This is the mistake the example demonstrates. An arrow function does not get its own this, so this will not be the car object.console.log(this); prints what this really is here. It is the this of the surrounding code (for example the browser’s global window object, or an empty object in Node), not car.return "Brand: " + this.brand; tries to read brand from that this. The global object has no brand property, so this.brand is undefined, and the returned text is Brand: undefined.} closes the arrow function body.}; closes the car object and ends the statement.car.describe() calls the method and console.log prints what it returns: Brand: undefined (after the line printed by console.log(this)). The fix is to write describe as a regular function, describe: function () { ... }, so that this is car and the result is Brand: Toyota.
Since describe is an arrow function, this
does not refer to car, so this.brand is
undefined instead of "Toyota".
Arrow Functions Don’t Have Their Own arguments Object
Regular functions have access to a built-in
arguments object containing all passed arguments.
Arrow functions do not have their own arguments
object; using rest parameters is the modern alternative.
What this code does: Arrow functions have no arguments object. Use rest parameters instead.
...numbers collects all arguments into an array, which is then summed with reduce.
const sumAll = (...numbers) => {
return numbers.reduce((total, current) => total + current, 0);
};
console.log(sumAll(1, 2, 3, 4));
const sumAll = (...numbers) => { uses a rest parameter: the three dots ... collect all the arguments passed to the function into a real array named numbers. Arrow functions have no arguments object, so a rest parameter is the way to accept any number of arguments.numbers.reduce((total, current) => total + current, 0) goes through the array and combines the items into a single value. The inner arrow function receives the running total and the current item and returns their sum. The 0 at the end is the starting value of total. For 1, 2, 3, 4 the total grows 0, 1, 3, 6, 10. The return sends the final total back.}; closes the function body and ends the assignment.sumAll(1, 2, 3, 4) collects 1, 2, 3 and 4 into numbers. The function returns 10, which is printed. You can pass any number of arguments.Arrow Functions vs. Regular Functions
| Aspect | Regular Function | Arrow Function |
|---|---|---|
| Syntax length | Longer | Shorter, supports implicit return |
| Own this binding | Yes | No (inherits from enclosing scope) |
| Own arguments object | Yes | No (use rest parameters instead) |
| Can be used as a constructor | Yes (with new) | No |
| Good for object methods using this | Yes | Generally no |
Practical Example
Let’s use arrow functions to process a small list of employee records.
What this code does: A practical chain: filter employees who worked more than 40 hours, then map to their names only. Arrow functions keep each step short and readable.
const employees = [
{ name: "Sofia", hoursWorked: 42 },
{ name: "James", hoursWorked: 38 },
{ name: "Lena", hoursWorked: 45 }
];
const overtimeEmployees = employees
.filter(employee => employee.hoursWorked > 40)
.map(employee => employee.name);
console.log("Worked overtime:", overtimeEmployees);
const employees = [ starts an array. Each item will be an object describing one employee.{ name: "Sofia", hoursWorked: 42 }, is an object with two properties. Sofia worked 42 hours.{ name: "James", hoursWorked: 38 }, James worked 38 hours.{ name: "Lena", hoursWorked: 45 } Lena worked 45 hours. There is no comma after the last item.]; closes the array and ends the statement.const overtimeEmployees = employees begins a method chain. The chain continues on the next lines, which makes it easy to read one step at a time. The final result will be stored in overtimeEmployees..filter(employee => employee.hoursWorked > 40) keeps only the employees whose hoursWorked is more than 40. The arrow function returns true or false for each employee. Sofia (42) and Lena (45) pass; James (38) is dropped. The result is a new array with just those two employee objects..map(employee => employee.name); takes the filtered employees and turns each one into just their name. The result is an array of text values, and the semicolon ends the whole chain.console.log("Worked overtime:", overtimeEmployees); prints the label and the array of names: Sofia and Lena.Common Mistakes With Arrow Functions
- Using an arrow function as an object method when the method needs to access
this. - Forgetting to wrap an implicitly returned object literal in parentheses, causing the curly braces to be parsed as a function body instead.
- Assuming arrow functions have their own
argumentsobject. - Trying to use an arrow function as a constructor with the
newkeyword, which throws aTypeError. - Overcomplicating a multi-step function body by forcing it into a single implicit-return expression instead of using curly braces and
return.
Best Practices for Arrow Functions
- Use arrow functions for short, simple callbacks, especially with array methods like
map(),filter(), andreduce(). - Avoid arrow functions for object methods that rely on
this. - Use implicit return only when it keeps the code readable; switch to curly braces and
returnfor anything more than a simple expression. - Use rest parameters (
...args) instead of relying on theargumentsobject. - Keep parameter parentheses consistent across your codebase, even for single parameters, for readability.
Quick Summary
- Arrow functions use
=>instead of thefunctionkeyword. - A single-expression body can omit curly braces and
return(implicit return). - Returning an object literal implicitly requires wrapping it in parentheses.
- Arrow functions inherit
thisfrom their surrounding scope instead of having their own. - Arrow functions don’t have their own
argumentsobject and cannot be used as constructors. - They are best suited for short callbacks, not object methods that depend on
this.
Try It Yourself
Modify the arrow function below. Try changing the calculation, or
converting it between implicit return and an explicit
return statement with curly braces.
What this code does: Practice example: change the conversion factor or switch between implicit and explicit return, then click Test My Code to see the new array of kilometers.
const distances = [5, 12, 3, 8];
const kilometers = distances.map(miles => miles * 1.60934);
console.log(kilometers);
const distances = [5, 12, 3, 8]; creates an array of distances measured in miles.distances.map(miles => miles * 1.60934) converts each distance to kilometers. One mile is about 1.60934 km. The single parameter miles needs no parentheses and the expression is returned automatically. Try changing the number, or rewrite it with curly braces and an explicit return: miles => { return miles * 1.60934; }.console.log(kilometers); prints the new array: 8.0467, 19.31208, 4.82802 and 12.87472. The original distances array is not changed.
Tip:
A quick way to decide between an arrow function and a regular
function: if you find yourself reaching for this to
refer to the object the function belongs to, use a regular
function instead.
Understanding arrow functions is essential before moving on to
closures, the this keyword in depth, and more
advanced JavaScript concepts.
🧪 Test Your JavaScript Code
Edit the code on the left and click “Run Code” to see the console output on the right.
Click “Run Code” to see the output here.
Code Explanation
The complete example stays visible. Use Previous and Next to move through its logical blocks.
Select Next to begin exploring the code.