JavaScript Arrow Functions
Learn arrow function syntax, implicit returns, and lexical this with clear practical examples.
TL;DR
- Write concise callbacks using
=>instead of thefunctionkeyword. - Skip
returnand braces for one-line expressions with implicit returns. - Inherit
thisfrom the enclosing scope instead of redefining it.
Basic Syntax and Returning Values
One parameterDrop parens when the function takes one parameter.
const double = n => n * 2;
double(4); // 8Multiple parametersWrap params in parens for two or more.
const multiply = (a, b) => a * b;
multiply(2, 5); // 10No parametersUse empty parens for no parameters.
const greet = () => 'Hello!';
greet(); // "Hello!"Use constAssign with const to prevent reassignment.
const sayHi = () => 'Hi';
sayHi = () => 'Yo'; // throws TypeErrorImplicit returnDrop braces and return for a single expression.
const sum = (a, b) => a + b;Block bodyUse braces with return for multi-line bodies.
const check = num => {
return num > 10 ? 'big' : 'small';
};Object literalWrap object literals in parens to avoid a syntax error.
const make = id => ({ id, active: true });Promise and Async Patterns
Promise chainsMost real-world arrow functions live inside .then(), .catch(), or async wrappers.
fetch('/api/user')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));Promise.all()Run independent async calls together and destructure results once every promise resolves.
const loadDashboard = async () => {
const [users, posts] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
]);
};Async arrowUse an arrow function as the body of an async function for compact definitions.
const getUser = async (id) => {
const res = await fetch(`/users/${id}`);
return res.json();
};Lexical This
Arrow inherits thisAn arrow function inherits this from its surrounding scope, so it stays bound.
function Timer() {
this.count = 0;
setInterval(() => this.count++, 1000);
}Regular function thisA regular function creates its own this, which can cause unexpected values.
setInterval(function() {
this.count++; // this is not the Timer
}, 1000);Class methodsThis behavior makes callbacks inside class methods work without manual binding.
class Counter {
count = 0;
increment = () => ++this.count;
}Method callbacksArrow callbacks inside a method inherit this from that method, not the caller.
const cart = { tax: 0.1, total(p) {
return p.map(n => n + n * this.tax);
} };
cart.total([100]); // [110]Limitations
No arguments objectArrow functions have no arguments object; use rest parameters to collect arguments instead.
function legacyLogger() {
return arguments.length;
}
const modernLogger = (...args) => args.length;No constructorsArrow functions cannot be used as constructors with the new keyword.
const Person = (name) => { this.name = name; };
new Person('Alex'); // throws: not a constructorNo generatorsArrow functions cannot be used as generator functions with yield.
function* range(n) {
for (let i = 0; i < n; i++) yield i;
}
[...range(3)]; // [0, 1, 2]No prototypeArrow functions have no prototype property, since they can never act as constructors.
const Greeter = () => {};
Greeter.prototype; // undefinedTips
- Use arrow functions for short callbacks inside
map()andfilter(), since they keep the code easy to scan. - Name longer arrow functions by assigning them to a
const, since stack traces then show the variable name.
Warnings
- Do not use arrow functions as methods on objects when those methods need access to the object through
this. - Arrow functions cannot be used as constructors, so calling one with
newthrows a TypeError instead of creating an instance.
In Practice
Chain arrow functions through filter and map to build a display-ready list of in-stock products with formatted prices.
filter()keeps only products whereinStockis true, using a one-line arrow function with an implicit return.map()returns a new object literal for each product, so the object needs to be wrapped in parentheses.- A template literal inside the arrow function formats each price to two decimal places.
- The result is a fresh array — the original
productsarray is never mutated.
const products = [
{ name: 'Desk Lamp', price: 24.5, inStock: true },
{ name: 'Notebook', price: 4.99, inStock: true },
{ name: 'Stapler', price: 12, inStock: false },
];
const display = products
.filter(p => p.inStock)
.map(p => ({ label: `${p.name} — $${p.price.toFixed(2)}` }));
console.log(display);
// [{ label: 'Desk Lamp — $24.50' }, { label: 'Notebook — $4.99' }]FAQ
Use arrow functions for short, inline callbacks like those passed to map(), filter(), or setTimeout(). Stick with regular functions when you need your own this binding, the arguments object, or when defining object methods.
Wrap the object literal in parentheses: const getUser = () => ({ name: 'Alice', age: 30 }). Without the parentheses, the curly braces are parsed as a function body instead of an object.
Arrow functions capture this from the enclosing lexical scope at definition time, not the call site. If you need this to refer to a specific object at runtime, use a regular function instead.
No, arrow functions cannot act as constructors and throw a TypeError when called with new. They also lack a prototype property, so use a regular function or a class to instantiate objects.