JavaScript Arrow Functions: A Complete ES6 Guide

Arrow functions are the concise ES6 alternative to traditional functions. Learn the syntax, the rules, and the key differences -- no own this, no arguments, no prototype.

7 min read

Arrow functions are a shorter, cleaner way to write function expressions. Introduced in ES6 (ES2015), they use the => syntax and come with important behavioral differences from regular functions. They are not just syntactic sugar, they change how this works and remove features like arguments and prototype.

Here is the same function written three ways, going from a regular function expression to its shortest arrow form. The starting point is a plain function expression:

javascriptjavascript
const add = function(a, b) {
  return a + b;
};

Replacing the function keyword with an arrow gives the first arrow version, still with braces and an explicit return statement inside the function body:

javascriptjavascript
const addArrow = (a, b) => {
  return a + b;
};

Once the body is a single expression, the braces and return keyword can be dropped entirely, since the arrow implies the return automatically:

javascriptjavascript
const addShort = (a, b) => a + b;

Three lines of boilerplate become one line of intent.

Arrow Function Syntax

The syntax has several valid forms depending on the number of parameters and the body type. With zero, one, or multiple parameters, the rules for parentheses differ slightly:

javascriptjavascript
// No parameters -- empty parentheses required
const sayHi = () => "Hi!";
 
// Single parameter -- parentheses optional
const double = n => n * 2;
 
// Multiple parameters -- parentheses required
const add = (a, b) => a + b;

Once the body needs more than a single expression, the syntax changes shape. A block body requires curly braces and an explicit return, just like a regular function:

javascriptjavascript
const greet = (name) => {
  const greeting = `Hello, ${name}!`;
  return greeting;
};

The rule is simple: if the body is a single expression, you get an implicit return and no braces. If the body contains statements, you need braces and an explicit return.

Implicit Return and Object Literals

The implicit return is one of the most useful features. But it has a gotcha when returning object literals. Writing the body as a bare object looks reasonable, but it does not do what it appears to:

javascriptjavascript
const makeUser = (name) => { name: name };
console.log(makeUser("Alex")); // undefined

JavaScript sees the opening curly brace right after the arrow and assumes a block body, so name: name is read as a label followed by an expression, not an object literal. Wrapping the object in parentheses tells the parser it is an expression instead:

javascriptjavascript
const makeUserFixed = (name) => ({ name: name });
console.log(makeUserFixed("Alex")); // { name: "Alex" }

This is one of the most common arrow function mistakes, and it is worth remembering the fix: parentheses around the object literal.

No Own this

This is the most important behavioral difference. Arrow functions do not have their own this. They inherit this from the enclosing scope at the time they are defined.

javascriptjavascript
const user = {
  name: "Sara",
  greetReg() {
    console.log(`Hi, I'm ${this.name}`);
  },
  greetArrow: () => {
    console.log(`Hi, I'm ${this.name}`);
  },
};

The regular method, greetReg, gets this bound to user at call time, so it can read the name property. The arrow function property, greetArrow, is defined differently: it never receives its own this at all, no matter how it is called.

javascriptjavascript
user.greetReg();   // "Hi, I'm Sara"
user.greetArrow(); // "Hi, I'm undefined"

The arrow function does not get user as its this. It captures this from where it was defined, the module scope, where the name property is undefined.

This behavior is exactly what you want for callbacks inside methods, where losing track of this would otherwise be a common source of bugs:

javascriptjavascript
const counter = {
  count: 0,
  start() {
    setInterval(() => {
      this.count++;
      console.log(this.count);
    }, 1000);
  },
};

The arrow function passed to setInterval inherits this from start, which is counter, so this.count correctly increments the object's own property on every tick:

javascriptjavascript
counter.start();
// 1, 2, 3, ...

With a regular function, this.count would be undefined because setInterval calls its callback with this set to the global object. The arrow function sidesteps this entirely by not having its own this.

No arguments Object

Arrow functions do not have the arguments object. Inside an arrow function, the name arguments refers to the enclosing function's arguments instead:

javascriptjavascript
function outer() {
  const inner = () => {
    console.log(arguments); // outer's arguments, not inner's
  };
  inner();
}
 
outer(1, 2, 3); // [Arguments] { '0': 1, '1': 2, '2': 3 }

If you need to capture arguments in an arrow function, use rest parameters instead. A rest parameter collects every argument into a real array that the arrow function can read directly:

javascriptjavascript
const sum = (...numbers) => numbers.reduce((a, b) => a + b, 0);
 
console.log(sum(1, 2, 3, 4)); // 10

Cannot Be Used as Constructors

Arrow functions do not have a prototype property and cannot be called with the new keyword:

javascriptjavascript
const Person = (name) => {
  this.name = name;
};
 
const p = new Person("Alex"); // TypeError: Person is not a constructor

Use a regular function or a class when you need a constructor.

No Line Break Before the Arrow

The arrow must be on the same line as the parameters:

javascriptjavascript
// SyntaxError
const addBroken = (a, b)
  => a + b;

Moving the line break to after the arrow instead fixes it, since the restriction only applies to the space between the parameter list and the arrow itself:

javascriptjavascript
// Valid: line break after the arrow
const addValid = (a, b) =>
  a + b;
 
// Also valid: wrap body in parentheses
const addWrapped = (a, b) => (
  a + b
);

Arrow Functions in Array Methods

Arrow functions truly shine in array methods like map, filter, and reduce, since the callback passed to each method is usually a short, throwaway piece of logic that does not need its own this or name:

javascriptjavascript
const numbers = [1, 2, 3, 4, 5];
 
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const sum = numbers.reduce((acc, n) => acc + n, 0);
 
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(evens);   // [2, 4]
console.log(sum);     // 15

It helps to see the same transformation written the older way, so the size difference is concrete rather than abstract. Compare with the regular function equivalent:

javascriptjavascript
const doubled = numbers.map(function(n) {
  return n * 2;
});

The arrow version is shorter, less noisy, and reads closer to the intent.

Arrow Functions in Promise Chains

Arrow functions keep promise chains clean:

javascriptjavascript
fetch("/api/users")
  .then(response => response.json())
  .then(users => users.filter(u => u.active))
  .then(activeUsers => console.log(activeUsers))
  .catch(error => console.error("Failed:", error));

Each then step is a single expression. The data flows left to right without the noise of the function and return keywords.

When Not to Use Arrow Functions

Arrow functions are not always the right choice. Avoid them for:

  • Object methods, since this will not point to the object.
  • Constructors, since arrow functions cannot be used with new.
  • Functions that need the arguments object, use rest parameters or a regular function instead.
  • Event handlers that rely on this, unless you specifically want lexical this.

For a full breakdown of when to avoid arrows, see When to Avoid Using Arrow Functions in JavaScript. For how arrow functions compare to other function forms, see JavaScript Function Expressions vs Declarations.

Rune AI

Rune AI

Key Insights

  • Arrow functions use => syntax and can have implicit returns when the body is a single expression.
  • They do not have their own this -- they inherit it from the enclosing scope.
  • They cannot be used as constructors, do not have arguments, and do not have a prototype.
  • Use parentheses around the body to return an object literal: () => ({ key: value }).
  • Arrow functions are ideal for callbacks, array methods, and promise chains.
RunePowered by Rune AI

Frequently Asked Questions

Can arrow functions be used as methods in objects?

Technically yes, but it is usually a mistake. Arrow functions do not have their own this, so this inside an arrow function method refers to the enclosing scope, not the object. Use the method shorthand syntax instead.

Do arrow functions work in older browsers?

Arrow functions are supported in all modern browsers since 2016. Internet Explorer does not support them. If you need IE support, use a transpiler like Babel.

Can I use new with an arrow function?

No. Arrow functions do not have a prototype property and throw a TypeError when called with new. Use a regular function or a class for constructors.

Conclusion

Arrow functions are not just shorter function expressions. They are semantically different: no own this, no arguments object, no prototype, and they cannot be constructors. Use them for short callbacks, array methods, and anywhere you want this to be lexically bound. Avoid them for object methods, constructors, and situations where you need the arguments object.