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.
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:
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:
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:
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:
// 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:
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:
const makeUser = (name) => { name: name };
console.log(makeUser("Alex")); // undefinedJavaScript 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:
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.
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.
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:
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:
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:
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:
const sum = (...numbers) => numbers.reduce((a, b) => a + b, 0);
console.log(sum(1, 2, 3, 4)); // 10Cannot Be Used as Constructors
Arrow functions do not have a prototype property and cannot be called with the new keyword:
const Person = (name) => {
this.name = name;
};
const p = new Person("Alex"); // TypeError: Person is not a constructorUse 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:
// 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:
// 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:
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); // 15It 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:
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:
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
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.
Frequently Asked Questions
Can arrow functions be used as methods in objects?
Do arrow functions work in older browsers?
Can I use new with an arrow function?
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.
More in this topic
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.
How to Loop Through Arrays Using JS for Loops Guide
Looping through arrays is the most practical use of JavaScript for loops. Learn how to access each element by index, avoid off-by-one errors, and choose between for, for...of, and forEach.