Default & rest parameters
Handle missing JavaScript arguments, collect any number of inputs, understand the old arguments object, spread arrays into calls, and design flexible functions.
- 01Choose safe defaultsKnow exactly when a default parameter runs, including
undefined,null, and0. - 02Collect many argumentsUse rest parameters instead of the old
argumentsobject for new code. - 03Unpack arrays into callsUse spread for calls like
Math.max(...scores)and for fixed-plus-many arguments.
The parameter toolbox
A parameter is the named input in a function declaration. An argument is the value you pass when you call it. You met that distinction in Declaring & calling functions. This lesson adds the tools that make function inputs pleasant in real programs.
JavaScript is forgiving: a missing argument becomes undefined, and extra arguments are usually ignored. That flexibility is useful, but it can make functions vague unless you choose a clear pattern:
- Default parameters fill in a value when an argument is missing.
- Rest parameters gather any number of arguments into a real array.
- Spread in a call does the opposite: it unpacks an array into separate arguments.
- The
argumentsobject is the older way to see everything that was passed. - Options objects help you simulate overloading without pretending JavaScript has overloaded signatures.
Picture an address form where the country field is already filled in. If you do nothing, the form uses that value. If you type something—even something odd—the typed value wins. A default parameter works the same way.
- In real life: A form shows Country: United States
- In JavaScript:
function ship(country = "United States") - In real life: Leave it blank and the form uses the pre-fill
- In JavaScript: Call with no argument or
undefined - In real life: Type Canada and the form uses Canada
- In JavaScript: Pass
"Canada"; the default is skipped - In real life: Type 0 by mistake and the form still records 0
- In JavaScript: Pass
0; defaults do not judge whether it is useful
Where the analogy stops: A real form might treat an empty field as missing. JavaScript parameter defaults are stricter: only a missing argument or the value undefined triggers them.
Keep one rule in your head as we start: defaults handle missing arguments, not every value that feels empty. That is different from some uses of || you may have seen, and it pairs nicely with the Nullish coalescing lesson’s idea of being precise about absence.
Default parameters
STEP THROUGHA default parameter places = value right in the function header. It reads like a promise: “if the caller does not give me this value, use this one.”
function greet(name = "friend") {
return "Hello, " + name;
}
greet(); // "Hello, friend"
greet("Ada"); // "Hello, Ada"The exact trigger matters. Step through this real run and switch the call on line 5. Notice that null and 0 do not mean “use the default.”
Change the call on line 5. Predict whether the default runs before you step.
script
function greet(name = "friend") { const message = "Hello, " + name; console.log(message);}?? are cousins, not twinsA parameter default runs when the argument is missing or exactly undefined. The ?? operator checks for null or undefined inside an expression. The ideas are related, but they are not interchangeable.
Later defaults can use earlier parameters because JavaScript fills them from left to right. This is handy for paired values:
function box(width, height = width) {
return width + " × " + height;
}
box(5); // "5 × 5"
box(5, 2); // "5 × 2"The pitfall is putting a default before a required-looking parameter: function label(prefix = "Item", text) { ... }. A call like label("Save") fills prefix, not text. Prefer required inputs first, defaults after them, or use an options object when there are several optional pieces.
Defaults run at call time
STEP THROUGHA default expression is evaluated when the function is called, not once when the function is created. That means Date.now() would get a fresh time, and [] creates a fresh array for that call.
Step through two calls. Watch the default array get made fresh each time.
script
function remember(item, list = []) { list.push(item); console.log(list.join(","));}remember("cake");This is good news. In some languages a default array can become shared state by accident. In JavaScript, list = [] inside the parameter list is made again for every call that needs it.
A fresh default does not mean every value you touch is private forever. If the caller passes an existing array, your function receives that actual array. Defaults only decide what to use when the caller leaves the argument out.
Rest parameters
INTERACTIVESometimes one function should accept any number of values: zero scores, three tags, five names, however many the caller has. A rest parameter uses three dots before the final parameter name:
function sum(...numbers) { let total = 0; for (const number of numbers) { total = total + number; } return total;}Call: sum(4, 8)
numbers array: [4, 8]
Array.isArray(numbers): true
Returned total: 12
The rest parameter creates numbers = [4, 8]. It is a real array, so its length is 2. The function returns 12.
The rest parameter must be last. JavaScript needs every argument after the fixed parameters to go into that one array, so there cannot be another parameter after it.
function log(level, ...messages) { // ✅ rest is last
return messages.length;
}
function bad(...items, last) { // ❌ SyntaxError
return last;
}Imagine packing for a picnic. One sandwich goes in your hand because it has a special job. Every other loose snack gets swept into a “misc” bag. Rest parameters do that for arguments.
- In real life: Pack the first item in your hand
- In JavaScript: A fixed parameter like
level - In real life: Sweep every remaining item into the bag
- In JavaScript:
...messagesgathers the rest - In real life: Look inside the bag and count items
- In JavaScript: Use array tools like
.length,.map, orfor...of
Where the analogy stops: A real bag can hold anything from anywhere. A rest parameter only gathers arguments from this one function call, and it must be the final parameter.
Because rest gives you a real array, the for...of loop you learned earlier fits perfectly. Arrays get their full treatment in Stage 3, but for now it is enough to know that a rest array has length, indexes, and array methods.
The arguments object
STEP THROUGHBefore rest parameters existed, JavaScript functions used the automatic arguments object. It lists every argument passed to an old-style function body. You will see it in older code, but new code should usually choose rest.
Step through the old arguments object, then compare it with rest parameters.
script
function inspect() { console.log(arguments.length); console.log(arguments[0]); console.log(Array.from(arguments).join(" | "));}arguments.map failsfunction broken() {
return arguments.map((value) => value);
}
broken(1, 2, 3);That snippet throws a TypeError because arguments is array-like, not a real array. Convert it first with Array.from(arguments), or better, write function broken(...values) and use values.map.
Arrow functions do not have their own arguments object. The Arrow functions lesson covers that properly; for now, it is another reason rest parameters are the modern habit.
Spread in calls
SORTThe same ... syntax has an opposite job in a function call. Rest gathers many arguments into one array. Spread unpacks one iterable value—like a simple array—into many arguments.
You cannot ask the cashier to scan a closed grocery bag. You unpack the bag onto the counter item by item. Spread does that for a function call.
- In real life: A bag of apples, bread, and milk
- In JavaScript: An array like
scores - In real life: Put each item on the counter separately
- In JavaScript:
...scoresin a call - In real life: The cashier scans each item
- In JavaScript: The function receives separate arguments
Where the analogy stops: Spread does not copy magic into the function. It only changes how the call is made: the function sees separate arguments, as if you typed them one by one.
const scores = [18, 7, 21];
console.log(Math.max(...scores));
console.log(Math.max(10, ...scores, 30));| Question | Rest | Spread |
|---|---|---|
| Where it appears | Function parameter list | Function call or array/object literal |
| Direction | Many arguments become one array | One array becomes many arguments |
| Example | function sum(...numbers) {} | sum(...numbers) |
| Common mistake | Putting another parameter after it | Forgetting the iterable must be unpackable |
function sum(...numbers) { }Math.max(...scores)function log(level, ...messages) { }send('info', ...messages)greet(...names)function tag(first, ...rest) { }
Sort each snippet by what the three dots are doing. Look at whether the dots are in a function header or in a call.
Simulating overloading
Some languages let you define several functions with the same name but different parameter lists. That is called overloading by signature. JavaScript does not do that. If you define the same function name twice, the later one wins.
Instead, write one function that decides what the caller meant. For small cases, branch on arguments.length, typeof, or Array.isArray. For readable real code, the modern favorite is an options object with defaults.
function createUser({ name = "Anonymous", role = "viewer" } = {}) {
return name + " (" + role + ")";
}
console.log(createUser({ name: "Ada" }));
console.log(createUser({ name: "Grace", role: "admin" }));
console.log(createUser());The braces in the parameter gently introduce destructuring: pull the name and role properties out of the object. The Destructuring lesson later gives that syntax a full tour. The = default means createUser() is safe, and role = "viewer" fills in the missing property.
Component props, configuration functions, loggers, formatters, and API helpers often grow optional settings. An options object keeps call sites clear: createUser({ name: "Ada", role: "admin" }) is easier to read than createUser("Ada", true, false, "admin").
Common misconceptions
“Defaults run for every falsy value.”
No. Only missing arguments and undefined trigger parameter defaults. null, 0, false, and empty strings are values the caller chose.
“Rest and spread are the same because both use three dots.”
Same punctuation, opposite jobs. Rest gathers arguments in a function header; spread unpacks values in a call.
“The arguments object is an array.”
It has indexes and length, but no array methods. Convert it or use rest parameters instead.
“Rest can appear anywhere in the parameter list.”
Rest must be last. Otherwise JavaScript could not know which arguments belong in the rest array and which belong after it.
“JavaScript picks the right function overload for me.”
JavaScript calls the function stored under that name. It does not choose by parameter types or count. Put the branching inside one function.
Practice: flexible function inputs
5 EXERCISESPredict the three printed lines. Type them separated by commas.
function label(value = "empty") {
console.log(value);
}
label(undefined);
label(null);
label(0);The calls print empty, then null, then 0. Defaults are not a general truthiness check.
average(...nums)Write a function that returns the average of any number of arguments. It should return 0 for no arguments.
function average(...nums) {
// return 0 when there are no numbers
}function average(...nums) {
if (nums.length === 0) return 0;
let total = 0;
for (const num of nums) {
total += num;
}
return total / nums.length;
}
console.log(average());
console.log(average(2, 4, 6));Rest gives nums as a real array. The zero-argument case returns 0 so you do not divide by zero.
arguments to restRewrite the function with a rest parameter, then check what listNames("Ada", "Grace") prints.
function listNames() {
return Array.from(arguments).join(", ");
}function listNames(...names) {
return names.join(", ");
}
console.log(listNames("Ada", "Grace"));The new version says what it expects right in the header and avoids the old arguments object.
Math.minUse spread to print the smallest number in the array.
const scores = [9, 4, 12];
// print the smallest scoreconst scores = [9, 4, 12];
console.log(Math.min(...scores));Spread unpacks the array into Math.min(9, 4, 12), so the smallest value is 4.
Build a function that accepts { name, role } and gives both properties defaults.
function createUser(/* options */) {
// name defaults to "Anonymous"
// role defaults to "viewer"
}function createUser({ name = "Anonymous", role = "viewer" } = {}) {
return name + " (" + role + ")";
}
console.log(createUser({ name: "Ada" }));
console.log(createUser({ name: "Grace", role: "admin" }));
console.log(createUser());The object default handles a missing argument. The property defaults handle missing name or role.
Quiz: check your understanding
7 QUESTIONSAnswer from the code, not from memory. Every wrong option explains the trap.
Question 1 of 7When does a default parameter value run?
Choose an answer to see the explanation.
Question 2 of 7What does
greet(null)print?Read the code, then predictfunction greet(name = "friend") { console.log("Hi " + name); } greet(null);Choose an answer to see the explanation.
Question 3 of 7Which function header is valid JavaScript?
Choose an answer to see the explanation.
Question 4 of 7What does rest array code print?
Read the code, then predictfunction count(...items) { console.log(Array.isArray(items)); console.log(items.length); } count("a", "b");Choose an answer to see the explanation.
Question 5 of 7Which statement about
argumentsis true?Choose an answer to see the explanation.
Question 6 of 7What does
Math.max(...scores)print?Read the code, then predictconst scores = [3, 8, 5]; console.log(Math.max(...scores));Choose an answer to see the explanation.
Question 7 of 7JavaScript has no overloading by signature. What is the modern beginner-friendly workaround?
Choose an answer to see the explanation.
Key takeaways
- Default parameters run only for missing arguments or
undefined. - Default expressions are evaluated at call time, so
[]is fresh for each call that needs it. - Rest parameters gather arguments into a real array and must be last.
argumentsis old, array-like, and absent from arrow functions’ own scope; prefer rest.- Spread in calls unpacks arrays into separate arguments.
- JavaScript simulates overloading by branching inside one function or by accepting an options object.
Remember the one-liner.
Defaults fill missing slots, rest gathers extra arguments, and spread unpacks a collection into a call.
Up next: Function expressions & callbacks.