Arrow functions use => syntax:
// Traditional function
function square(x) {
return x * x;
}
// Arrow function
const square = (x) => x * x;
Both do the same thing, but the arrow version is shorter.
Basic Syntax
Single parameter, single expression:
const double = x => x * 2;
No parentheses needed around single parameter. Implicit return.
Multiple parameters:
const add = (a, b) => a + b;
Parentheses required for multiple parameters.
No parameters:
const getRandom = () => Math.random();
Empty parentheses required.
Multiple statements:
const greet = (name) => {
const message = `Hello, ${name}!`;
return message;
};
Curly braces and explicit return needed for multiple statements.
Implicit Return
Single-expression arrows return automatically:
const square = x => x * x; // Returns x * x
Equivalent to:
const square = x => {
return x * x;
};
But returning object literals requires parentheses:
const makePerson = name => ({ name: name }); // Parentheses needed
Without parentheses, JavaScript interprets {} as a block, not an object.
The 'this' Difference
This is the crucial difference. Arrow functions don't have their own this. They inherit this from the enclosing scope:
function Person() {
this.age = 0;
setInterval(function() {
this.age++; // 'this' is window/global, not Person
}, 1000);
}
// Fixed with arrow function
function Person() {
this.age = 0;
setInterval(() => {
this.age++; // 'this' is Person instance
}, 1000);
}
Traditional functions create their own this. Arrow functions use the this from where they're defined.
Lexical this Binding
"Lexical" means the value is determined by where the function is written, not where it's called:
const obj = {
name: 'Alice',
traditional: function() {
console.log(this.name); // 'Alice'
},
arrow: () => {
console.log(this.name); // undefined (this is global/window)
}
};
obj.traditional(); // Works
obj.arrow(); // Doesn't work as expected
The arrow function's this was set when the object was created (global scope), not when the method was called.
When to Use Arrow Functions
Callbacks and array methods:
[1, 2, 3].map(x => x * 2);
[1, 2, 3].filter(x => x > 1);
[1, 2, 3].reduce((sum, x) => sum + x, 0);
Event handlers where you need outer this:
class Button {
constructor() {
this.count = 0;
document.querySelector('button').addEventListener('click', () => {
this.count++; // 'this' is Button instance
});
}
}
Promises and async code:
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data));
When NOT to Use Arrow Functions
Object methods:
const person = {
name: 'Bob',
greet: () => {
console.log(`Hi, I'm ${this.name}`); // 'this' is not person
}
};
// Use traditional function instead
const person = {
name: 'Bob',
greet() {
console.log(`Hi, I'm ${this.name}`); // Works correctly
}
};
Prototype methods:
Person.prototype.greet = () => {
console.log(this.name); // Wrong 'this'
};
// Use traditional function
Person.prototype.greet = function() {
console.log(this.name); // Correct 'this'
};
Constructors:
const Person = (name) => {
this.name = name; // Error: arrow functions can't be constructors
};
new Person('Alice'); // TypeError
No arguments Object
Arrow functions don't have an arguments object:
function traditional() {
console.log(arguments); // Works
}
const arrow = () => {
console.log(arguments); // ReferenceError
};
// Use rest parameters instead
const arrow = (...args) => {
console.log(args); // Works
};
Can't Be Used with new
Arrow functions aren't constructors:
const Person = (name) => {
this.name = name;
};
new Person('Alice'); // TypeError: Person is not a constructor
No prototype Property
Arrow functions don't have .prototype:
const arrow = () => {};
console.log(arrow.prototype); // undefined
function regular() {}
console.log(regular.prototype); // Object
Performance
Arrow functions are slightly faster to create (no this binding, no arguments object, no prototype). The difference is negligible in most code.
Style Considerations
Some teams prefer consistency—always arrow or always traditional. Others use arrow for callbacks and traditional for methods.
Airbnb style guide recommends:
- Arrow functions for callbacks
- Traditional functions for top-level functions and methods
Async Arrow Functions
Arrow functions work with async/await:
const fetchData = async () => {
const response = await fetch('/api/data');
return response.json();
};
Higher-Order Functions
Arrow functions excel at functional programming:
// Function composition
const compose = (f, g) => x => f(g(x));
const add1 = x => x + 1;
const double = x => x * 2;
const add1ThenDouble = compose(double, add1);
add1ThenDouble(3); // 8
Currying
Arrow syntax makes currying readable:
const add = a => b => a + b;
const add5 = add(5);
add5(3); // 8
Equivalent to:
function add(a) {
return function(b) {
return a + b;
};
}
Common Mistakes
Forgetting parentheses for object returns:
const makePerson = name => { name: name }; // Wrong - empty block
const makePerson = name => ({ name: name }); // Correct
Using arrow for methods needing dynamic this:
const button = {
text: 'Click me',
click: () => {
console.log(this.text); // undefined
}
};
Expecting arguments object:
const fn = () => {
console.log(arguments); // ReferenceError
};
Browser Support
Arrow functions work in all modern browsers. IE11 doesn't support them. Transpile with Babel if IE11 support is needed.
Further Reading
MDN's arrow function documentation covers all behaviors and edge cases.
The ECMAScript specification defines arrow function semantics precisely.
Kyle Simpson's You Don't Know JS: ES6 & Beyond has a chapter on arrow functions and their this binding.
Arrow functions are now the standard for callbacks and functional code in JavaScript.
0 comments