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.
- 01Use defaultsSay exactly when a default parameter is used: for a missing argument or
undefined, but not fornullor0. - 02Collect many argumentsUse a rest parameter to collect any number of arguments into an array, instead of the old
argumentsobject. - 03Pass an array's valuesUse spread to pass an array's values as separate arguments, like
Math.max(...scores).
Five tools for function inputs
A parameter is a name in the brackets of a function, like customer in function showOrder(customer). An argument is the value you pass in when you call the function, like "Asha". You met both in Declaring & calling functions. This lesson shows five tools that make function inputs easier to work with.
JavaScript doesn't check how many arguments you pass. A missing argument becomes undefined. An extra argument is ignored, unless the function asks for it. The five tools let a function say clearly what it wants:
- Default parameters give a parameter a value to use when its argument is missing.
- Rest parameters collect any number of arguments into one array. An array is a list of values in square brackets, like
[1, 2, 3]. - Spread in a call does the opposite. It takes the values out of an array and passes them as separate arguments.
- The
argumentsobject is an older way to see every argument that was passed. - An options object passes many settings as one object, each with a name. It lets one function be called in several ways.
function showOrder(customer, drink) {
console.log(customer, drink);
}
showOrder("Asha", "tea");
showOrder("Asha");
showOrder("Asha", "tea", "cake");Line 1 makes a function with two parameters, customer and drink. Line 2 prints both. Line 4 passes two arguments, so customer is "Asha" and drink is "tea". It prints Asha tea.
Line 5 passes only one argument, so drink is undefined. It prints Asha undefined. Line 6 passes three arguments. The function has no third parameter, so "cake" is ignored. It prints Asha tea.
"Asha" goes into customer and "tea" goes into drink. There is no third parameter, so "cake" is ignored.Think of an address form on a shop website. The Country field already says United States. If you leave it, the form uses United States. If you type something else, the form uses what you typed, even if it is odd. A default parameter works the same way.
- In real life: The Country field already says United States
- In JavaScript:
function ship(country = "United States") - In real life: You leave the field as it is, so the form uses United States
- In JavaScript: Call
ship()with no argument, or passundefined - In real life: You type Canada, so the form uses Canada
- In JavaScript: Pass
"Canada"; the default is not used - In real life: You type 0 by mistake, and the form still saves 0
- In JavaScript: Pass
0; a default doesn't check whether a value makes sense
Keep one rule in mind for this lesson: a default is used only when an argument is missing. It is not used for every value that looks empty, like 0 or "". This is different from ||, which you may have seen used for fallback values. || replaces any falsy value: a value that counts as false in an if.
Default parameters
STEP THROUGHTo give a parameter a default, write = value after its name, inside the function's brackets. It means: "If the caller doesn't give me this value, use this one."
function greet(name = "friend") {
return "Hello, " + name;
}
greet(); // "Hello, friend"
greet("Ada"); // "Hello, Ada"Line 1 gives name the default "friend". Line 5 calls greet() with no argument, so name is "friend". Line 6 passes "Ada", so the default is not used.
Which values use the default, exactly? Step through this run. You can change the call on line 5. Watch null and 0 closely: they do not use the default.
Change the call on line 5. Guess whether the default is used, then step through.
script
function greet(name = "friend") { const message = "Hello, " + name; console.log(message);}"friend" is used only when the argument is missing or undefined. null is a real value, so name keeps null.A default parameter is used when the argument is missing or exactly undefined. The ?? operator gives a fallback when a value is null or undefined. So they are close, but they treat null differently.
A default can use a parameter that comes before it. JavaScript fills parameters from left to right, so the earlier one already has its value. This helps when two values are often the same:
function box(width, height = width) {
return width + " × " + height;
}
box(5); // "5 × 5"
box(5, 2); // "5 × 2"Line 1 says: if height is missing, use width. Line 5 passes only 5, so it returns "5 × 5". Line 6 passes both values, so it returns "5 × 2".
Be careful with order. Look at function label(prefix = "Item", text). The call label("Save") puts "Save" into prefix, the first parameter, not into text. So text is undefined. Put the parameters the caller must give first, and the ones with defaults after them. If there are many optional values, use an options object. You'll see one later in this lesson.
A default is made fresh for each call
STEP THROUGHJavaScript works out a default's value each time a call needs it. It does not work it out once, when the function is made. So Date.now() as a default would give the current time for each call. And [] as a default makes a new, empty array for each call.
Step through both calls. Watch each call get its own new array.
script
function remember(item, list = []) { list.push(item); console.log(list.join(","));}remember("cake");Lines 5 and 6 each call remember with no list. Each call gets its own new array. So the second call prints cake, not tea,cake. Some other languages make a default array only once and share it between calls. JavaScript doesn't.
The new array is made only when the caller leaves the argument out. If the caller passes an array, the function gets that same array, not a copy. Changes made inside the function show up outside it too.
function update(name, user) {
name = "Mina";
user.name = "Mina";
}
const name = "Asha";
const user = { name: "Asha" };
update(name, user);
console.log(name);
console.log(user.name);Line 7 calls update with a string and an object. The parameter name gets its own copy of the string "Asha". Line 2 changes only that copy, so line 8 still prints Asha.
The parameter user points to the same object as the outer user. Line 3 changes that object's name property, so line 9 prints Mina.
Rest parameters
INTERACTIVESometimes a function should take any number of values: no scores, three tags, or five names. A rest parameter does this. Write three dots before the last parameter's name, like ...numbers. Every argument that is left over goes into an array with that 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 makes numbers = [4, 8]. It is a real array with 2 items. The function adds them up and returns 12.
Pick a few calls above. Each time, numbers is a real array, even when it is empty.
sum(1, 2, 3) passes three separate values. The rest parameter ...numbers collects them into one array, [1, 2, 3].A rest parameter must be the last parameter. It takes every argument that is left. If another parameter came after it, no argument would be left for that one.
function log(level, ...messages) { // OK: rest is last
return messages.length;
}
function bad(...items, last) { // SyntaxError: rest is not last
return last;
}Line 1 is fine: level takes the first argument, and messages takes the rest. Line 5 puts last after the rest parameter. JavaScript refuses to run this code and throws a SyntaxError. A syntax error means the code is not written in a form JavaScript accepts.
You pack for a picnic. The sandwich goes in your hand, because it has its own job. Every other snack goes into one bag. A rest parameter is that bag, for arguments.
- In real life: You hold the sandwich in your hand
- In JavaScript: A normal parameter like
leveltakes one argument - In real life: Every other snack goes into one bag
- In JavaScript:
...messagescollects the rest - In real life: You look in the bag and count the snacks
- In JavaScript: Use array tools like
.lengthorfor...of
A rest parameter is a real array, so you can loop over it with the for...of loop from earlier. Arrays get their own lessons in Stage 3. For now, you only need to know that a rest array has:
- a
length, which says how many values it holds, - numbered positions starting at 0, like
numbers[0], - array methods, like
.joinand.map.
The old arguments object
STEP THROUGHBefore rest parameters existed, functions used the arguments object. JavaScript makes it for you inside every function written with the function keyword. It holds every argument that was passed. You'll see it in older code. In new code, use a rest parameter instead.
Step through the old arguments object. Then compare it with a rest parameter.
script
function inspect() { console.log(arguments.length); console.log(arguments[0]); console.log(Array.from(arguments).join(" | "));}Line 2 prints how many arguments there are: 3. Line 3 reads the first one, "Ada", at position 0. Line 4 turns arguments into a real array with Array.from, then joins the names into one string.
arguments.map failsfunction broken() {
return arguments.map((value) => value);
}
broken(1, 2, 3);Line 2 throws a TypeError. The arguments object is array-like: it has numbered positions and a length, like an array. But it doesn't have array methods such as .map.
To fix it, turn it into an array first with Array.from(arguments). Or better, write function broken(...values) and use values.map.
Arrow functions, like (x) => x * 2, don't get their own arguments object. The Arrow functions lesson explains this. It is one more reason to use rest parameters in new code.
Spread in a function call
SORTThe same three dots do the opposite job inside a function call. Rest collects many arguments into one array. Spread takes one array and passes each of its values as a separate argument. Spread works on arrays, and on other values you can loop over with for...of, like strings.
The cashier can't scan a closed bag. You take each item out and put it on the counter. Spread does that for a function call.
- In real life: A bag with apples, bread, and milk
- In JavaScript: An array like
scores - In real life: Put each item on the counter, one by one
- In JavaScript: Write
...scoresin a call - In real life: The cashier scans each item
- In JavaScript: The function gets each value as its own argument
const scores = [18, 7, 21];
console.log(Math.max(...scores));
console.log(Math.max(10, ...scores, 30));Line 1 makes an array of three scores. Math.max is a built-in function that returns the biggest of its arguments. It wants separate numbers, not one array.
Line 2 spreads the array, so it works like Math.max(18, 7, 21) and prints 21. Line 3 mixes normal arguments with spread. It works like Math.max(10, 18, 7, 21, 30) and prints 30.
...scores takes the values out of the array, so Math.max gets three separate arguments: 18, 7, and 21.Rest and spread both use three dots, so they are easy to mix up. Each row of the table below asks one question and answers it for rest and for spread.
| Question | Rest | Spread |
|---|---|---|
| Where do the dots go? | In a function's parameter list. | In a function call. You can also use them inside a new array or object you are writing. |
| Which way do values move? | Many arguments go in, and one array comes out. | One array goes in, and many separate arguments come out. |
| What does it look like? | function sum(...numbers) {} | sum(...numbers) |
| What is a common mistake? | Putting another parameter after it. | Spreading a value that isn't a list, like a number. That throws a TypeError. |
function sum(...numbers) { }Math.max(...scores)function log(level, ...messages) { }send('info', ...messages)greet(...names)function tag(first, ...rest) { }
Sort each line by what its three dots do. Ask: are the dots in a function's parameter list, or in a call?
One function, many ways to call it
In some languages, you can write several functions with the same name but different parameters. The language picks one, based on what you pass. This is called overloading. JavaScript doesn't have it. If you write two functions with the same name, the second one replaces the first.
Instead, write one function that checks what it was given. For small cases, it can check:
arguments.length: how many arguments came in,typeof: what type a value is, like"string",Array.isArray: whether a value is an array.
For real code, the most common way is an options object: one object argument with named settings, each with a default.
function createUser({ name = "Anonymous", role = "viewer" } = {}) {
return name + " (" + role + ")";
}
console.log(createUser({ name: "Ada" }));
console.log(createUser({ name: "Grace", role: "admin" }));
console.log(createUser());Line 1 has one parameter: an object in curly braces. The braces take the name and role properties out of that object and make them into variables. This is called destructuring. A later lesson covers it fully.
nameandroleeach have their own default.- The
= {}at the end gives the whole object a default. SocreateUser()with nothing passed doesn't throw.
Line 4 gives only a name, so role is "viewer". Line 5 gives both. Line 6 gives nothing, so both defaults are used, and it prints Anonymous (viewer).
Many real functions grow optional settings: a logger, a date formatter, or a helper that talks to a server. An options object keeps each call easy to read. Compare createUser({ name: "Ada", role: "admin" }) with createUser("Ada", true, false, "admin"). In the second call, you can't tell what true and false mean.
function badge({ label = "New", count = 0 } = {}) {
return label + ":" + count;
}
console.log(badge({ label: "Cart" }));
console.log(badge({ label: undefined, count: null }));Line 4 leaves out count, so count uses its default, 0. It prints Cart:0.
Line 5 passes undefined for label, so label uses its default, "New". It passes null for count, and null is kept. It prints New:null.
Common mix-ups
"Defaults are used for every falsy value."
No. A default is used only when the argument is missing or undefined. Falsy values count as false in an if, but null, 0, false, and "" are still values the caller chose. They are kept.
"Rest and spread are the same, because both use three dots."
No. They do opposite jobs. Rest is in a function's parameter list and collects arguments into an array. Spread is in a call and takes values out of an array.
"The arguments object is an array."
No. It has numbered positions and a length, but no array methods like .map. Turn it into an array with Array.from, or use a rest parameter.
"A rest parameter can go anywhere in the list."
No. It must be last. It takes every argument that is left, so no argument could reach a parameter after it.
"JavaScript picks the right version of a function for me."
No. JavaScript calls whatever function is saved under that name. It doesn't look at the types or the number of arguments. Put the checks inside one function.
Practice: flexible function inputs
5 EXERCISESStart by guessing what a short program prints. Then write small functions that use rest, spread, and an options object. Try each one first, then use the hints if you get stuck.
Guess the three printed lines. Type them in order, separated by commas.
function label(value = "empty") {
console.log(value);
}
label(undefined);
label(null);
label(0);The calls print empty, then null, then 0. A default only replaces a missing or undefined argument. It doesn't replace every value that counts as false.
average(...nums)Write a function that returns the average of any number of arguments. With no arguments, it should return 0.
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));The rest parameter makes nums a real array. With no numbers, the function returns 0, so it never divides by zero.
arguments with a rest parameterRewrite 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 shows what it takes, right in its first line. It doesn't need 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 turns the call into Math.min(9, 4, 12), so the smallest number, 4, is printed.
Write a function that takes { name, role } and gives both properties a default.
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 = {} default is used when no object is passed. The defaults inside the braces are used when name or role is missing.
Quiz: check your understanding
7 QUESTIONSWork out each answer from the code, not from memory. Every answer explains why it is right or wrong.
Question 1 of 7When is a default parameter value used?
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 of these functions is valid JavaScript?
Choose an answer to see the explanation.
Question 4 of 7What does this rest parameter 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 can't pick between two functions with the same name. How do you let one function be called in several ways?
Choose an answer to see the explanation.
Key takeaways
- A default parameter is used only when its argument is missing or
undefined. - JavaScript works out a default during each call, so
[]makes a new array for every call that needs it. - A rest parameter collects the leftover arguments into a real array. It must be the last parameter.
argumentsis older and array-like, and arrow functions don't have their own. Use a rest parameter instead.- Spread in a call takes the values out of an array and passes them as separate arguments.
- JavaScript has no overloading. Write one function that checks its inputs, or have it take an options object.
Remember the one-liner.
Defaults fill in missing arguments, rest collects extra arguments, and spread takes values out of an array for a call.
Up next: Function expressions & callbacks.