JavaScript Rest Parameters: A Complete Tutorial

Learn how to use JavaScript rest parameters to accept any number of function arguments. Covers the ...rest syntax, combining rest with regular parameters, rest vs the arguments object, rest vs spread operator, and practical variadic function patterns.

JavaScriptbeginner
10 min read

Rest parameters let a function accept any number of arguments and collect them into a real array. The syntax uses three dots (...) before a parameter name: function sum(...numbers). Unlike the legacy arguments object, rest parameters produce a proper array with access to map, filter, reduce, and every other array method. They work in both regular and arrow functions, making them the modern standard for variadic functions.

This tutorial covers rest parameter syntax, rules, comparisons with the arguments object and spread operator, and practical patterns you will use in real projects.

Basic Rest Parameter Syntax

The ... before a parameter name collects all remaining arguments into an array:

javascriptjavascript
function sum(...numbers) {
  console.log(numbers);          // [1, 2, 3, 4, 5]
  console.log(Array.isArray(numbers)); // true
  return numbers.reduce((total, n) => total + n, 0);
}
 
console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(sum(10, 20));         // 30
console.log(sum());               // 0

When no arguments are passed, the rest parameter is an empty array, not undefined:

javascriptjavascript
function test(...args) {
  console.log(args);        // []
  console.log(args.length); // 0
}
 
test();

Combining Rest with Named Parameters

Rest parameters collect only the arguments that do not match named parameters:

javascriptjavascript
function sendMessage(sender, ...recipients) {
  console.log(`From: ${sender}`);
  console.log(`To: ${recipients.join(", ")}`);
}
 
sendMessage("Alice", "Bob", "Charlie", "Diana");
// From: Alice
// To: Bob, Charlie, Diana
javascriptjavascript
function logEvent(level, timestamp, ...details) {
  const formatted = `[${level.toUpperCase()}] ${timestamp}`;
  details.forEach((detail) => console.log(`${formatted} - ${detail}`));
}
 
logEvent("info", "2026-02-05", "Server started", "Port 3000", "Ready");
// [INFO] 2026-02-05 - Server started
// [INFO] 2026-02-05 - Port 3000
// [INFO] 2026-02-05 - Ready

The Rest Parameter Must Be Last

JavaScript enforces a single rule: the rest parameter must be the final parameter.

javascriptjavascript
// Valid: rest is last
function valid(a, b, ...rest) {}
 
// SyntaxError: Rest parameter must be last formal parameter
// function invalid(...rest, a, b) {}
 
// SyntaxError: can't have two rest parameters
// function alsoInvalid(...a, ...b) {}
PatternValid?Reason
function f(...args)YesSingle rest parameter
function f(a, ...args)YesRest is last
function f(a, b, ...args)YesRest is last
function f(...args, a)NoRest is not last
function f(...a, ...b)NoMultiple rest parameters

Rest Parameters in Arrow Functions

Arrow functions support rest parameters with the same syntax:

javascriptjavascript
const multiply = (...numbers) => {
  return numbers.reduce((product, n) => product * n, 1);
};
 
console.log(multiply(2, 3, 4)); // 24
console.log(multiply(5, 10));   // 50
javascriptjavascript
// Concise arrow with rest
const first = (...args) => args[0];
const last = (...args) => args[args.length - 1];
 
console.log(first(10, 20, 30)); // 10
console.log(last(10, 20, 30));  // 30
Arrow Functions Need Rest Parameters

Arrow functions do not have their own arguments object. If you need to access all arguments in an arrow function, rest parameters are the only option. This is one key reason rest parameters have replaced the arguments object in modern JavaScript.

Rest vs the arguments Object

The arguments object is a legacy feature available in regular functions. Rest parameters are the modern replacement:

javascriptjavascript
// Legacy: arguments object
function legacySum() {
  let total = 0;
  for (let i = 0; i < arguments.length; i++) {
    total += arguments[i];
  }
  return total;
}
 
// Modern: rest parameters
function modernSum(...numbers) {
  return numbers.reduce((total, n) => total + n, 0);
}
FeatureargumentsRest parameters
TypeArray-like objectReal Array
Array.isArray()falsetrue
.map(), .filter(), .reduce()Not availableAvailable
.forEach()Not availableAvailable
Works in arrow functionsNoYes
Named and descriptiveNo (always arguments)Yes (...items, ...errors)
Includes all argumentsYes (all of them)Only remaining ones
Can destructureNoYes
ES versionES1 (1997)ES6 (2015)

Converting arguments to a Real Array

When working with legacy code, you may need to convert arguments:

javascriptjavascript
function legacyExample() {
  // Method 1: Array.from
  const arr1 = Array.from(arguments);
 
  // Method 2: spread into array
  const arr2 = [...arguments];
 
  // Method 3: Array.prototype.slice (oldest approach)
  const arr3 = Array.prototype.slice.call(arguments);
 
  return arr1; // all three produce the same result
}

Rest vs Spread Operator

Rest and spread both use ... but do opposite things:

javascriptjavascript
// REST: collects individual items INTO an array (in function parameters)
function sum(...numbers) {
  return numbers.reduce((total, n) => total + n, 0);
}
 
// SPREAD: expands an array INTO individual items (in function calls)
const values = [1, 2, 3, 4];
console.log(sum(...values)); // 10
ContextNameDirectionExample
Function parameterRestCollects into arrayfunction f(...args)
Function callSpreadExpands out of arrayf(...array)
Array literalSpreadExpands into new array[...arr1, ...arr2]
Object literalSpreadExpands into new object{ ...obj1, ...obj2 }
javascriptjavascript
// They work together naturally
function merge(...arrays) {
  // rest collects all arguments into one array of arrays
  return arrays.reduce((result, arr) => [...result, ...arr], []);
  // spread expands each inner array
}
 
console.log(merge([1, 2], [3, 4], [5, 6]));
// [1, 2, 3, 4, 5, 6]

Destructuring with Rest Parameters

Rest syntax works inside destructuring assignments to capture remaining elements:

Array Destructuring with Rest

javascriptjavascript
function processScores(first, ...remaining) {
  console.log(`Highest: ${first}`);
  console.log(`Others: ${remaining}`);
}
 
processScores(98, 85, 72, 91);
// Highest: 98
// Others: 85,72,91

Object Destructuring with Rest

javascriptjavascript
function processUser({ name, email, ...extras }) {
  console.log(`Name: ${name}`);
  console.log(`Email: ${email}`);
  console.log("Extra fields:", extras);
}
 
processUser({
  name: "Alice",
  email: "alice@test.com",
  age: 28,
  role: "admin",
  active: true,
});
// Name: Alice
// Email: alice@test.com
// Extra fields: { age: 28, role: "admin", active: true }
Rest Destructuring for Removing Properties

Rest destructuring is a clean way to exclude properties from an object without mutation: const { password, ...safeUser } = user; creates a new object without the password field.

Practical Patterns

Flexible Event Logger

javascriptjavascript
function logEvent(eventName, ...data) {
  const timestamp = new Date().toISOString();
  const entry = {
    event: eventName,
    timestamp,
    data: data.length === 1 ? data[0] : data,
  };
 
  console.log(JSON.stringify(entry, null, 2));
  return entry;
}
 
logEvent("click", { button: "submit", page: "/signup" });
// { event: "click", timestamp: "...", data: { button: "submit", page: "/signup" } }
 
logEvent("error", 404, "Not Found", "/api/users");
// { event: "error", timestamp: "...", data: [404, "Not Found", "/api/users"] }

The pipe pattern above demonstrates returning functions from functions, a powerful technique for composing operations:

javascriptjavascript
function pipe(...fns) {
  return function (input) {
    return fns.reduce((result, fn) => fn(result), input);
  };
}
 
const processName = pipe(
  (name) => name.trim(),
  (name) => name.toLowerCase(),
  (name) => name.replace(/\s+/g, "-")
);
 
console.log(processName("  John DOE  ")); // "john-doe"

Safe Math Operations

javascriptjavascript
function safeMax(...values) {
  const numbers = values.filter((v) => typeof v === "number" && !isNaN(v));
 
  if (numbers.length === 0) {
    return null;
  }
 
  return Math.max(...numbers);
}
 
console.log(safeMax(3, 1, 4, 1, 5)); // 5
console.log(safeMax(3, "hello", NaN, 7)); // 7
console.log(safeMax());                // null

Wrapper Functions

Rest parameters make it easy to create wrapper functions that pass arguments through. This pattern is central to higher-order functions:

javascriptjavascript
function withLogging(fn) {
  return function (...args) {
    console.log(`Calling ${fn.name} with:`, args);
    const result = fn(...args);
    console.log(`Result:`, result);
    return result;
  };
}
 
function add(a, b) {
  return a + b;
}
 
const loggedAdd = withLogging(add);
loggedAdd(3, 5);
// Calling add with: [3, 5]
// Result: 8

Merge Multiple Objects

javascriptjavascript
function mergeObjects(...objects) {
  return objects.reduce((merged, obj) => ({ ...merged, ...obj }), {});
}
 
const defaults = { theme: "light", lang: "en", fontSize: 14 };
const userPrefs = { theme: "dark", fontSize: 16 };
const overrides = { fontSize: 18 };
 
console.log(mergeObjects(defaults, userPrefs, overrides));
// { theme: "dark", lang: "en", fontSize: 18 }

Common Mistakes

Putting Rest Before Other Parameters

javascriptjavascript
// SyntaxError
// function wrong(...args, callback) {}
 
// Correct: rest must be last
function correct(callback, ...args) {
  args.forEach((arg) => callback(arg));
}

Confusing Rest and Spread

javascriptjavascript
// This is REST (collecting arguments)
function sum(...nums) {
  return nums.reduce((a, b) => a + b, 0);
}
 
// This is SPREAD (expanding an array)
const numbers = [1, 2, 3];
sum(...numbers); // 6
 
// NOT rest (this is spread inside an array literal)
const combined = [...numbers, 4, 5]; // [1, 2, 3, 4, 5]
Rune AI

Rune AI

Key Insights

  • ...args creates a real array: use map, filter, reduce directly on rest parameters
  • Rest must be last: it collects all remaining arguments after named parameters
  • Rest replaces arguments: works in arrow functions, is a true array, and has a descriptive name
  • Rest and spread are opposites: rest collects, spread expands, both use ...
  • Empty calls produce []: rest parameters default to an empty array, not undefined
RunePowered by Rune AI

Frequently Asked Questions

Can a function have more than one rest parameter?

No. A function can only have one rest parameter, and it must be the last parameter in the list. `function f(...a, ...b)` is a SyntaxError. If you need to split arguments into groups, use a single rest parameter and then slice [the array](/tutorials/programming-languages/javascript/how-to-loop-through-arrays-using-js-for-loops-guide): `const [first, ...remaining] = args;`.

Do rest parameters affect the function's .length property?

Yes. The `.length` property of a function counts only the parameters before the rest parameter. `function f(a, b, ...rest) {}` has `.length` of 2, not 3. The rest parameter is not counted.

Can I use rest parameters with default parameters?

Yes, but they serve different purposes. [Default parameter](/tutorials/programming-languages/javascript/how-to-use-default-parameters-in-js-functions)s provide fallback values for named parameters. Rest parameters collect remaining arguments into an array. They combine naturally: `function f(a = 1, ...rest) {}`.

What happens if I use a rest parameter with no extra arguments?

The rest parameter becomes an empty array `[]`, not `undefined`. This means you can safely call array methods on it without checking if it exists: `args.forEach(...)` works even when no arguments are passed.

Is there a performance difference between rest and arguments?

In modern engines, the difference is negligible. Rest parameters may be slightly faster because the engine knows upfront that it needs to create an array. The `arguments` object has legacy behaviors (like being linked to named parameters in non-[strict mode](/tutorials/programming-languages/javascript/javascript-strict-mode-use-strict-explained)) that engines must account for.

Conclusion

Rest parameters collect function arguments into a real array using the ...name syntax. They must be the last parameter in the function definition, produce a true Array instance, and work in both regular and arrow functions. They replace the legacy arguments object, which is array-like but lacks array methods. Rest and spread use the same ... syntax but work in opposite directions: rest collects values into an array, spread expands an array into individual values.