Arrow Functions and Function Expressions
Arrow functions and function expressions are modern ways to define functions in JavaScript. Understanding their differences is important.
Function Expressions
A function expression assigns a function to a variable:
const add = function(a, b) {
return a + b;
};
console.log(add(2, 3)); // 5
Named Function Expressions
const factorial = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1);
};
console.log(factorial(5)); // 120
Anonymous Function Expressions
const greet = function(name) {
return `Hello, ${name}!`;
};
console.log(greet("Alice")); // "Hello, Alice!"
Arrow Functions
Arrow functions provide concise syntax:
const add = (a, b) => {
return a + b;
};
console.log(add(2, 3)); // 5
Concise Syntax
For single expressions, omit braces and return:
const add = (a, b) => a + b;
console.log(add(2, 3)); // 5
const square = x => x * x;
console.log(square(4)); // 16
const greet = () => "Hello!";
console.log(greet()); // "Hello!"
Multiple Parameters
// Multiple parameters need parentheses
const multiply = (a, b) => a * b;
// Single parameter - parentheses optional
const double = x => x * 2;
const double2 = (x) => x * 2; // Also valid
// No parameters - parentheses required
const random = () => Math.random();
Multiple Statements
const calculate = (a, b) => {
const sum = a + b;
const product = a * b;
return { sum, product };
};
console.log(calculate(3, 4)); // { sum: 7, product: 12 }
Returning Objects
// Wrong - looks like code block
// const createUser = name => { name: name };
// Correct - wrap in parentheses
const createUser = name => ({ name });
console.log(createUser("Alice")); // { name: "Alice" }
Key Differences
1. this Binding
Arrow functions don’t have their own this:
const person = {
name: "Alice",
greet: function() {
console.log(this.name); // "Alice"
},
greetArrow: () => {
console.log(this.name); // undefined (this is global)
}
};
person.greet(); // "Alice"
person.greetArrow(); // undefined
2. arguments Object
Arrow functions don’t have arguments:
const regularFunc = function() {
console.log(arguments); // [1, 2, 3]
};
const arrowFunc = () => {
console.log(arguments); // ReferenceError
};
regularFunc(1, 2, 3);
Use rest parameters instead:
const arrowFunc = (...args) => {
console.log(args); // [1, 2, 3]
};
arrowFunc(1, 2, 3);
3. new Keyword
Arrow functions can’t be used as constructors:
const RegularFunc = function(name) {
this.name = name;
};
const user = new RegularFunc("Alice"); // Works
const ArrowFunc = (name) => {
this.name = name;
};
const user2 = new ArrowFunc("Bob"); // TypeError
4. Hoisting
Function expressions are not hoisted:
console.log(add(2, 3)); // TypeError: add is not a function
const add = (a, b) => a + b;
Function declarations are hoisted:
console.log(add(2, 3)); // 5
function add(a, b) {
return a + b;
}
When to Use Each
Use Arrow Functions
For callbacks and simple operations:
// Good - arrow function for callback
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(x => x * 2);
// Good - arrow function for simple operation
const add = (a, b) => a + b;
Use Function Expressions
When you need this binding:
const button = document.getElementById("myButton");
button.addEventListener("click", function() {
console.log(this); // button element
});
// Wrong - arrow function loses this
button.addEventListener("click", () => {
console.log(this); // window object
});
Use Function Declarations
For main functions and methods:
// Good - function declaration
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
// Good - method in object
const calculator = {
add: function(a, b) {
return a + b;
}
};
Practical Examples
Array Methods
const users = [
{ id: 1, name: "Alice", age: 30 },
{ id: 2, name: "Bob", age: 25 },
{ id: 3, name: "Charlie", age: 35 }
];
// map
const names = users.map(user => user.name);
console.log(names); // ["Alice", "Bob", "Charlie"]
// filter
const adults = users.filter(user => user.age >= 30);
console.log(adults); // [Alice, Charlie]
// reduce
const totalAge = users.reduce((sum, user) => sum + user.age, 0);
console.log(totalAge); // 90
Event Handlers
// Function expression - can use this
const button = {
element: document.getElementById("btn"),
init: function() {
this.element.addEventListener("click", function() {
console.log(this); // button element
});
}
};
// Arrow function - loses this
const button2 = {
element: document.getElementById("btn"),
init: function() {
this.element.addEventListener("click", () => {
console.log(this); // button2 object
});
}
};
Promises and Async
// Arrow functions work well with promises
fetch("/api/users")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
// Async arrow function
const fetchUsers = async () => {
try {
const response = await fetch("/api/users");
return await response.json();
} catch (error) {
console.error(error);
}
};
Currying
// Arrow functions for currying
const add = a => b => a + b;
const add5 = add(5);
console.log(add5(3)); // 8
// Multiply function
const multiply = a => b => a * b;
const double = multiply(2);
console.log(double(5)); // 10
Composition
const compose = (f, g) => x => f(g(x));
const addOne = x => x + 1;
const double = x => x * 2;
const addOneThenDouble = compose(double, addOne);
console.log(addOneThenDouble(5)); // 12 (5 + 1 = 6, 6 * 2 = 12)
Comparison Table
| Feature | Function Declaration | Function Expression | Arrow Function |
|---|---|---|---|
| Syntax | function name() {} |
const name = function() {} |
const name = () => {} |
| Hoisting | Yes | No | No |
| this binding | Own | Own | Lexical |
| arguments | Yes | Yes | No (use rest) |
| Constructor | Yes | Yes | No |
| Concise syntax | No | No | Yes |
| Best for | Main functions | Callbacks | Simple operations |
Best Practices
Use Arrow Functions for Callbacks
// Good
const numbers = [1, 2, 3];
const doubled = numbers.map(x => x * 2);
// Avoid
const doubled2 = numbers.map(function(x) {
return x * 2;
});
Use Function Expressions for Methods
// Good
const obj = {
name: "Alice",
greet: function() {
return `Hello, ${this.name}!`;
}
};
// Avoid
const obj2 = {
name: "Bob",
greet: () => {
return `Hello, ${this.name}!`; // this is wrong
}
};
Be Consistent
// Good - consistent style
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" }
];
const names = users.map(user => user.name);
const ids = users.map(user => user.id);
// Avoid - mixing styles
const names2 = users.map(function(user) { return user.name; });
const ids2 = users.map(user => user.id);
Summary
- Function expressions: assign function to variable
- Arrow functions: concise syntax with
=> - Arrow functions: lexical
thisbinding - Arrow functions: no
argumentsobject - Arrow functions: can’t be constructors
- Use arrow functions: for callbacks, simple operations
- Use function expressions: when you need
this - Use function declarations: for main functions
Related Resources
Official Documentation
Next Steps
- The ’this’ Keyword and Context Binding
- Array Methods: map, filter, reduce, forEach
- Callbacks and Asynchronous JavaScript
Comments