When I was learning JavaScript, rest parameters and the arguments object felt confusingly similar.
Here's a quick breakdown.
Why do we even need it
Imagine you're writing a function to calculate the total price of items in a shopping cart.
function calculateTotal(firstItemPrice, secondItemPrice, thirdItemPrice) {
let total = 0;
total = total + firstItemPrice + secondItemPrice + thirdItemPrice
return total;
}
calculateTotal(10, 20, 30); // 60
This works perfectly, except for the fact that it works perfectly only for 3 items. What if we need to calculate the price of 5 items? Or of 10 items? We really wouldn't want to pass 10 similar arguments to a function.. We don't even know how many items a user will put in a cart in the first place. And this is when the arguments object and rest parameters come in handy.
You need arguments object and rest parameters when you don't know how many parameters will be passed to a function in advance.
Arguments Object Usage
JavaScript gives us a built-in way to handle an unknown number of arguments - the arguments object. Think of it this way: it packs all the arguments you pass to a function inside just one variable.
// you don't need to declare arguments here
function calculateTotal() {
let total = 0;
// all the parameters you pass are inside arguments
// let's iterate over all items to count their total price
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
calculateTotal(10, 20, 30); // 60
calculateTotal(5, 15, 25, 35); // 80
Now we can count the total price of any number of items!
If you look carefully at the function we've just created you might want to use .reduce() here. Let's see how it'll work:
function calculateTotal() {
return arguments.reduce((total, price) => total + price, 0);
}
calculateTotal(10, 20, 30);
// TypeError: arguments.reduce is not a function
And this is exactly why arguments is so confusing.. We've just successfully iterated over it, we can even address any parameter with its index (for example, extract first item with arguments[0]) then why do we get this error?
If you try to use any other array method or iterator, you’ll run into the same error:
function calculateTotal() {
arguments.forEach(price => {
console.log(price);
});
}
calculateTotal(10, 20, 30);
// TypeError: arguments.forEach is not a function
function calculateTotal() {
return arguments.map(price => price * 2);
}
calculateTotal(10, 20, 30);
// TypeError: arguments.map is not a function
That’s because arguments is array-like, but NOT an actual array.
It supports:
numeric indexes (
arguments[0])length
But it doesn’t support:
- array methods (
map,reduce,forEach)
If you want to use arguments like an array, you first need to convert it into a real array with Array.from():
function calculateTotal() {
const prices = Array.from(arguments);
return prices.reduce((total, price) => total + price, 0);
}
calculateTotal(10, 20, 30); // 60
At this point we:
received arguments
converted them into an array
and only then used array methods
We're also using additional memory by creating a new array.
It all feels like extra, unnecessary work for something so common.
If you already think that using arguments is not very convenient, let me give you more reason to doubt it:
arguments does NOT exist in arrow functions
const calculateTotal = () => {
console.log(arguments);
};
calculateTotal(10, 20, 30);
// ReferenceError: arguments is not defined
Arrow functions simply don’t have their own arguments object.
So if you try to handle a dynamic number of parameters in an arrow function, you can’t use arguments at all.
What we have for now:
Pros:
Can handle an unknown number of arguments
You can access any argument by its index (
arguments[0])You can check how many arguments were passed using
arguments.length
Cons:
Not a real array: you need to convert it to an array first to use array methods like
.map(),.reduce()orforEachConverting to an array uses extra memory
Can't be used in arrow functions
And this is exactly why rest parameters exist.
Rest Parameters Usage
Rest parameters solve literally all the issues that arguments has.
Rest parameters let you collect all remaining function arguments into a real array right from the start.
const calculateTotal = (...prices) => {
prices.forEach(price => {
console.log(price);
});
};
calculateTotal(10, 20, 30);
// 10
// 20
// 30
Notice how:
We don't use
argumentsanymorepricesis already a real arrayWorks with any number of arguments, similarly to the arguments object
We can use rest parameters in an arrow function
There is even more advanced usage available - you can mix regular arguments with rest parameters:
// declare first argument separately
const calculateTotal = (firstItem, ...otherItems) => {
console.log(`First item purchased: ${firstItem}`);
if (otherItems.length > 0) {
console.log("Other items purchased:");
otherItems.forEach((item, index) => {
console.log(`${index + 1}. ${item}`)
})
}
else {
console.log("No other items purchased.")
}
}
// Example usage with numbers as prices:
calculateTotal(50, 20, 15, 10);
// First item purchased: 50
// Other items purchased:
// 20
// 15
// 10
firstItem:
- Captures the first argument separately
...otherItems :
Collects all remaining arguments into a real array
Allows iteration with
.forEach(),.map(),.reduce(), etc.
Unlike the arguments object, which captures everything all together, rest parameters must always be the last argument in your function definition. function(a, ...b, c) will throw a SyntaxError:
// SyntaxError: Rest parameter must be last formal parameter
function calculate(first, ...others, last) {
console.log(others);
}
// How the JS engine sees it:
// "Okay, the first argument is 10. Then 'others' captures everything that's left.
// Then what am I supposed to put into 'last'? I'm confused!"
If you want to separately use the last parameter, you can do it this way:
function calculate(first, ...others) {
// using index to address the last parameter
console.log(others[others.length-1]);
}
Don't confuse rest parameters with spread
Even though rest parameters (...args) and the spread operator (...myArray) use the same ... syntax, they do very different things.
Rest Parameters
- Used in a function parameter list to collect all the arguments into a single array
function sum(first, ...others) {
return others.reduce((total, num) => total + num, first);
}
sum(10, 20, 30, 40); // 100
Here:
first= 10...others=[20, 30, 40]- collects remaining arguments into an array.
Spread Operator
- Used when calling functions, creating arrays, or objects to expand elements.
const numbers = [20, 30, 40];
console.log(...numbers); // 20 30 40
const allNumbers = [10, ...numbers, 50];
console.log(allNumbers); // [10, 20, 30, 40, 50]
Here ...numbers “spreads” the array elements into individual values. It does not collect anything - it expands.
Quick Rule of Thumb:
Rest parameters → collects multiple values into an array + used only in functions
Spread → expand an array into individual elements
Rest Parameters vs arguments key differences
| Feature | arguments |
Rest Parameters |
|---|---|---|
| Type | Array-like | Real array |
| Works in arrow functions | No | Yes |
| Modern usage | Legacy | Preferred |
| Supports array methods | No | Yes |
The
argumentsobject is a legacy feature from older versions of JavaScript. It can still be used. Rest parameters (...args) are a newer feature in JavaScript (ES6+), designed to replace most use cases ofarguments. Use rest parameters by default.
Top comments (0)