Skip to main content

Command Palette

Search for a command to run...

JavaScript Arrow Functions: Write Cleaner Code the Modern Way

Stop Writing Verbose Functions. There Is a Cleaner Way.

Updated
15 min read
JavaScript Arrow Functions: Write Cleaner Code the Modern Way

If you have been writing JavaScript for a little while, you already know what a function is. You have written them, called them, maybe even passed them around. But at some point, you probably came across code that looked like this:

const add = (a, b) => a + b;

No function keyword. No curly braces. No return statement. And yet, it works perfectly.

That is an arrow function. And by the end of this post, you will not only understand what it is, you will actually prefer writing them.


What Are Arrow Functions?

Arrow functions are a shorter, cleaner way to write functions in JavaScript. They were introduced in ES6 (2015) and have become one of the most commonly used features in modern JavaScript.

They do not replace regular functions entirely, but for a large portion of what you write day to day, they are a much more readable alternative.

Think of them as a shorthand. Instead of writing out the full function keyword syntax every time, arrow functions let you express the same idea with less noise.


Starting Point: The Regular Function

Before we look at arrow functions, let us take a regular function and use it as our baseline. We will keep converting it throughout this post.

function greet(name) {
  return "Hello, " + name + "!";
}

console.log(greet("Sara")); // Hello, Sara!

Simple enough. Now let us see how this looks as an arrow function.


Basic Arrow Function Syntax

Here is the same greet function written as an arrow function:

const greet = (name) => {
  return "Hello, " + name + "!";
};

console.log(greet("Sara")); // Hello, Sara!

Let us break down what changed:

  1. The function keyword is gone

  2. We store the function in a const variable

  3. Between the parameters and the body, there is a => (that is the "arrow")

You might wonder why const and not let or var. The reason is simple: you almost never need to reassign a function to a different value after you define it. Using const makes that intention clear and prevents accidental overwrites. You will see this convention everywhere in modern JavaScript code.

That is the core syntax. Parameters on the left, arrow in the middle, function body on the right.

const functionName = (parameters) => {
  // function body
  return value;
};

It looks a little different at first, but after a few examples it starts to feel natural.


Arrow Functions With One Parameter

Here is something nice: when your function only has one parameter, you can drop the parentheses around it.

// With parentheses (always valid)
const double = (number) => {
  return number * 2;
};

// Without parentheses (also valid when there's only one param)
const double = number => {
  return number * 2;
};

console.log(double(5)); // 10

Both versions work exactly the same. Most developers drop the parentheses when there is a single parameter since it reads more cleanly. You will see both styles in real codebases, so it is good to recognise them.


Arrow Functions With Multiple Parameters

When you have two or more parameters, the parentheses are required. No way around it.

const multiply = (a, b) => {
  return a * b;
};

console.log(multiply(4, 3)); // 12

And when there are zero parameters, you use an empty pair of parentheses:

const sayHello = () => {
  return "Hello there!";
};

console.log(sayHello()); // Hello there!

So to summarise the parentheses rule:

Number of Parameters Parentheses
Zero () required
One Optional
Two or more () required

Implicit Return vs Explicit Return

This is where arrow functions get really powerful, and also where beginners sometimes get confused. Let us take it slow.

Explicit Return

An explicit return is what you already know. You write the return keyword, and it sends back a value.

const square = (n) => {
  return n * n;
};

console.log(square(6)); // 36

The curly braces create the function body, and return explicitly hands back the result. This is called explicit because you are spelling it out.

Implicit Return

When your function body is a single expression, you can skip the curly braces and the return keyword entirely. JavaScript will automatically return the result of that expression.

const square = (n) => n * n;

console.log(square(6)); // 36

No curly braces. No return. It just works. This is called an implicit return because the return is implied, not written.

Let us see a few more examples side by side:

// Explicit return
const addTen = (n) => {
  return n + 10;
};

// Implicit return (same function, cleaner)
const addTen = (n) => n + 10;


// Explicit return
const isEven = (n) => {
  return n % 2 === 0;
};

// Implicit return (same function, cleaner)
const isEven = (n) => n % 2 === 0;


console.log(addTen(5));    // 15
console.log(isEven(4));    // true
console.log(isEven(7));    // false

One thing to watch out for: if you want to implicitly return an object literal, you need to wrap it in parentheses. Otherwise JavaScript thinks the curly braces are a function body.

// This will NOT work as expected
const getUser = (name) => { name: name };

// This WILL work
const getUser = (name) => ({ name: name });

console.log(getUser("Ali")); // { name: 'Ali' }

The Core Difference: Arrow Function vs Regular Function

There are a few differences between arrow functions and regular functions, but for now, at your stage of learning, the most important one to understand is the syntax difference and when to use each.

Here is a direct comparison:

// Regular function declaration
function add(a, b) {
  return a + b;
}

// Regular function expression
const add = function(a, b) {
  return a + b;
};

// Arrow function
const add = (a, b) => a + b;

All three do the exact same thing. The arrow function version is simply the most concise.

Here is another comparison with a slightly more real-world example:

const numbers = [1, 2, 3, 4, 5];

// Using a regular function with map()
const doubled = numbers.map(function(num) {
  return num * 2;
});

// Using an arrow function with map()
const doubled = numbers.map(num => num * 2);

console.log(doubled); // [2, 4, 6, 8, 10]

The arrow function version reads almost like plain English. "Map each num to num times 2." That readability is a big reason why modern JavaScript code leans heavily on arrow functions.

A Note on this

You might have heard that arrow functions handle this differently from regular functions. That is true, and it matters in certain situations. But it is a topic of its own, and you do not need it right now. For simple functions, callbacks, and array methods, arrow functions behave exactly as you would expect. We will save the this conversation for another day.

Hoisting: A Real Difference That Will Catch You Off Guard

This is one of those things that does not come up in tutorials, but will definitely trip you up in real code.

Regular function declarations are hoisted. That means JavaScript loads them into memory before it runs your code, so you can call a regular function before the line where it is defined.

// This works fine
console.log(greet("Ali")); // "Hello, Ali!"

function greet(name) {
  return "Hello, " + name;
}

Arrow functions stored in const are not hoisted the same way. If you try to call one before its definition, you get a ReferenceError.

// This throws: ReferenceError: Cannot access 'greet' before initialization
console.log(greet("Ali"));

const greet = name => "Hello, " + name;

The fix is simple: always define your arrow functions before you use them. Just make it a habit to put your function definitions at the top of the file or scope, and you will never run into this issue.

Named vs Anonymous: What Shows Up in Error Messages

When something goes wrong in your code, JavaScript shows you a stack trace. With a regular function declaration, the function name appears in that trace, which makes debugging easier.

function calculateTotal(price) {
  return price * tax; // tax is not defined
}

calculateTotal(100);
// TypeError: tax is not defined
//   at calculateTotal (<anonymous>:2:18)  <-- name shows up

Arrow functions are technically anonymous, even when assigned to a variable. Modern JavaScript engines are smart enough to infer the name from the variable, so in most cases you will still see the name in errors. But in older environments or more complex situations, the name might not appear. This is a minor point, but worth knowing so you are not confused when you see <anonymous> in a stack trace.

Arrow Functions Cannot Be Used as Constructors

If you have ever used new to create an object from a function, that is called a constructor function. Arrow functions do not support this. Trying to use new with an arrow function will throw an error.

const Person = (name) => {
  this.name = name;
};

const user = new Person("Sara");
// TypeError: Person is not a constructor

For creating objects with new, use a regular function or a class. Arrow functions are simply not built for that job.

The arguments Object

Regular functions have access to a built-in arguments object that holds all the values passed into the function, even if you did not define named parameters for them.

function showArgs() {
  console.log(arguments);
}

showArgs(1, 2, 3); // [Arguments] { '0': 1, '1': 2, '2': 3 }

Arrow functions do not have their own arguments object. If you try to use it inside an arrow function, you will either get an error or accidentally access the arguments of a surrounding regular function, which is rarely what you want.

const showArgs = () => {
  console.log(arguments); // ReferenceError: arguments is not defined
};

If you need to handle an unknown number of arguments in an arrow function, use the rest parameter syntax instead. It is actually the cleaner modern approach anyway.

const showArgs = (...args) => {
  console.log(args); // a plain array
};

showArgs(1, 2, 3); // [1, 2, 3]

When to Use Arrow Functions vs Regular Functions

Now that you know the differences, here is a practical rule of thumb that will serve you well as a beginner.

Use an arrow function when:

  • You are writing a short, single-purpose function

  • You are passing a function as a callback (inside map(), filter(), forEach(), event listeners, etc.)

  • You want concise, readable code for simple operations

Use a regular function when:

  • You need to define a reusable named function that will be called from multiple places

  • You are writing a constructor function (used with new)

  • You need access to the arguments object

  • You are defining a method inside an object and need this to refer to the object

In practice, most of the functions you write as a beginner will fit comfortably into arrow functions. The cases where you need a regular function will become more obvious as you grow.


Arrow Functions as Callbacks

This is where arrow functions truly shine. Any time you pass a function as an argument to another function, that is a callback. Arrow functions make callbacks clean and readable.

You already saw map(). Here are filter() and forEach() as well, since you will use all three constantly.

const numbers = [1, 2, 3, 4, 5, 6, 7, 8];

// map() - transform every item
const squared = numbers.map(n => n * n);
console.log(squared); // [1, 4, 9, 16, 25, 36, 49, 64]

// filter() - keep only items that pass the test
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4, 6, 8]

// forEach() - run something for each item (does not return a new array)
numbers.forEach(n => console.log(n + " is in the list"));

Compare how these would look with regular functions:

// With regular functions
const squared = numbers.map(function(n) {
  return n * n;
});

const evens = numbers.filter(function(n) {
  return n % 2 === 0;
});

Both work. But arrow functions remove the visual noise and let you focus on the logic, not the syntax.


Here is a visual breakdown of the transformation:

NORMAL FUNCTION
=====================================
function greet ( name ) {
   |        |     |      |
keyword  name  param  body start

  return "Hello, " + name;
    |
  explicit return

}
body end


ARROW FUNCTION
=====================================
const greet = ( name ) => "Hello, " + name;
  |      |      |      |       |
const  name   param  arrow  implicit return
              (opt.
              for 1
              param)
FULL SYNTAX BREAKDOWN
=====================================

const  functionName  =  (params)  =>  { return value; }
  |         |        |     |      |         |
stored   variable  assign  args  arrow  explicit body


SHORT FORM (single expression):

const  functionName  =  param  =>  expression
                                       |
                               implicit return

This is where learning actually happens. Close the article, open your code editor, and try these yourself.

1. Convert a regular function to an arrow function

Start with this:

function square(n) {
  return n * n;
}

Rewrite it as an arrow function. Then rewrite it again using an implicit return.

// Your arrow function with explicit return
const square = (n) => {
  // your code here
};

// Your arrow function with implicit return
const square = (n) => // your code here;

Solution:

// Explicit return
const square = (n) => {
  return n * n;
};

// Implicit return
const square = (n) => n * n;

console.log(square(5)); // 25

2. Even or odd checker

Write an arrow function called checkEvenOdd that takes a number and returns the string "even" if the number is even, and "odd" if it is not.

// Write your function here
const checkEvenOdd = (n) => // your code here

console.log(checkEvenOdd(4)); // "even"
console.log(checkEvenOdd(7)); // "odd"

Solution:

const checkEvenOdd = (n) => n % 2 === 0 ? "even" : "odd";

console.log(checkEvenOdd(4)); // "even"
console.log(checkEvenOdd(7)); // "odd"

3. Use an arrow function inside map()

You have an array of names. Use map() with an arrow function to create a new array where each name is greeted.

const names = ["Ali", "Sara", "John"];

// Expected output: ["Hello, Ali!", "Hello, Sara!", "Hello, John!"]
const greetings = names.map(/* your arrow function here */);

console.log(greetings);

Solution:

const names = ["Ali", "Sara", "John"];

const greetings = names.map(name => "Hello, " + name + "!");

console.log(greetings);
// ["Hello, Ali!", "Hello, Sara!", "Hello, John!"]

Quick Reference

Here is a cheat sheet you can come back to:

// Zero parameters
const sayHi = () => "Hi!";

// One parameter (parentheses optional)
const double = n => n * 2;

// Multiple parameters
const add = (a, b) => a + b;

// Multi-line body (needs curly braces + return)
const describe = (name, age) => {
  const message = name + " is " + age + " years old.";
  return message;
};

// Returning an object (wrap in parentheses)
const makeUser = (name) => ({ name: name });

Wrapping Up

Arrow functions are one of those features that feel a bit foreign when you first see them, but within a week of using them they become second nature. Here is everything this post covered:

  • Arrow functions are a shorter syntax for writing functions, introduced in ES6

  • Store them in const because functions almost never need to be reassigned

  • One parameter means parentheses are optional; zero or two or more require them

  • Implicit return lets you skip curly braces and return for single-expression functions

  • They work especially well in callbacks and array methods like map(), filter(), and forEach()

  • Regular function declarations are hoisted; arrow functions are not, so always define them before calling them

  • Arrow functions cannot be used as constructors with new

  • Arrow functions do not have their own arguments object; use rest parameters instead

  • Use a regular function when you need a named, hoisted, constructor-capable function; use arrow functions for everything else

Start using them in your own code today. Even just converting one or two functions in something you are already working on will help the syntax stick. The more you write them, the more natural they feel.

Good luck, and keep writing code.

More from this blog