Lesson 14 / 21
Parameters
Missing and extra arguments, the condition under which default values kick in, rest parameters and spread, the arguments object, and destructured parameters.
Contents
In the previous lesson, functions were called with a fixed number of arguments. JavaScript is loose about this: a missing argument throws no error, and neither does an extra one. The cost of this looseness is that a missing argument stays silent.
This lesson covers parameter rules. The Programming Fundamentals course’s Value and Reference, and Passing lesson established the parameter–argument distinction and the concept of a variable number of arguments; here, which syntax JavaScript meets these concepts with, and which traps it carries, are shown.
Missing and Extra Arguments
A parameter not given at the call site gets the value undefined. An extra argument
given is silently ignored:
function report(station, threshold) { return `${station} / ${threshold}`; } console.log(report("A1")); console.log(report("A1", 20, "extra"));
A1 / undefined A1 / 20
Neither behavior produced an error. This is a deliberate decision of the language and
makes it possible for functions to behave differently based on argument count. Its cost
is that an argument left missing by a typo is only noticed once it carries an
undefined value into a later operation and breaks it.
Default Values
A parameter can be given a default value. The default kicks in only when the
argument is undefined — whether it is missing or explicitly given as undefined.
function report(station, threshold = 20) { return `${station} / ${threshold}`; } console.log(report("A1")); console.log(report("A1", undefined)); console.log(report("A1", null)); console.log(report("A1", 0)); console.log(report.length);
A1 / 20 A1 / 20 A1 / null A1 / 0 1
The third and fourth lines show that this is a narrower criterion than even the ??
operator from the Truthiness lesson: not even null triggers the default. The value
0 also passes through — default values naturally avoid the trap that a fallback
assignment written with || would fall into.
The last line shows that the length property gives the parameter count before
the parameter with a default.
Default expressions are re-evaluated on every call and can use the parameters that come before them:
let counter = 0; function next() { counter += 1; return counter; } function label(name, sequence = next()) { return `${name}-${sequence}`; } console.log(label("A1")); console.log(label("A2")); console.log(label("B1", 99)); console.log(label("B2"));
A1-1 A2-2 B1-99 B2-3
In the third call, next was never called because an argument was given; the counter
did not increase. This is proof that a default is evaluated at call time and only when
needed.
A default resting on a previous parameter is useful in range definitions:
function range(low, high = low + 5) { return [low, high]; } console.log(range(18)); console.log(range(18, 30));
[ 18, 23 ] [ 18, 30 ]
The order is one-directional: a parameter can refer only to parameters written before it.
A default expression being evaluated at call time also opens a way to declare required parameters:
function required(name) { throw new Error(`${name} parameter is required`); } function save(station = required("station"), temperature = required("temperature")) { return `${station}=${temperature}`; } console.log(save("A1", 21.5)); try { save("A1"); } catch (error) { console.log(error.name + ": " + error.message); }
A1=21.5 Error: temperature parameter is required
When an argument is given, the default expression never runs; when it is not given, an
error is thrown. Error objects and the throw statement are covered in this topic’s
last lesson.
Rest Parameters and Spread
Arguments whose count is not known in advance are collected with a rest parameter. The three-dot spelling puts all remaining arguments into a real array:
function highest(label, ...readings) { return `${label}: ${Math.max(...readings)} (${readings.length} readings)`; } console.log(highest("A1", 21.5, 19.75, 23)); console.log(highest("B1")); console.log(highest.length);
A1: 23 (3 readings) B1: -Infinity (0 readings) 1
The second line shows an edge case: called with no readings, readings is an empty
array, and Math.max() gives -Infinity. This is mathematically consistent — the
maximum of an empty set returns the neutral element — but is meaningless in a
measurement context. An empty-list check should be written.
The same three dots work in the opposite direction at the call site: they spread an array into separate arguments.
function highest(label, ...readings) { return `${label}: ${Math.max(...readings)}`; } const temperatures = [21.5, 19.75, 23, 18.25]; console.log(highest("all", ...temperatures));
all: 23
The two uses should not be confused. Three dots in a parameter list collect; three dots at a call site spread. The same syntax’s two directions are told apart by where they sit.
A rest parameter can only be in the last position and is not counted toward the
length property.
The Arguments Object
Before rest parameters, a variable number of arguments was read through an implicit
object called arguments. This object still exists and is found in older code:
function oldForm() { console.log(typeof arguments, arguments.length); console.log(Array.isArray(arguments)); console.log(Array.from(arguments)); } oldForm(21.5, 19.75);
object 2 false [ 21.5, 19.75 ]
The second line is the problem itself: arguments is not an array. It has a length
and indices, but no array methods; it has to be converted to an array first to be used.
A second problem shows up with default values:
function withDefault(a = 1) { console.log("arguments.length:", arguments.length, "a:", a); } withDefault(); withDefault(5);
arguments.length: 0 a: 1 arguments.length: 1 a: 5
arguments only counts the arguments actually given; it does not see a parameter
filled in by a default. The two sources of information diverge, and which one to read
becomes unclear.
Third, arguments is not found at all in arrow functions — this topic’s next subject.
In this course, a variable number of arguments will always be written with a rest
parameter.
Destructured Parameters
In a function taking more than three or four parameters, the call line becomes unreadable: the order has to be memorized. The solution is to gather the options into an object and unpack it in the parameter list through destructuring:
function summarize({ station, temperature, unit = "C" }) { return `${station}: ${temperature} ${unit}`; } console.log(summarize({ station: "A1", temperature: 21.5 })); console.log(summarize({ station: "A2", temperature: 19.75, unit: "K" }));
A1: 21.5 C A2: 19.75 K
The call line now states which value is which on its own, and order no longer matters. Defaults can be given to individual fields one by one.
This spelling has a trap: if the argument is not given at all, destructuring tries to
operate on undefined:
function summarize({ station, temperature }) { return `${station}: ${temperature}`; } try { console.log(summarize()); } catch (error) { console.log(error.name + ": " + error.message); }
TypeError: Cannot destructure property 'station' of 'undefined' as it is undefined.
The fix is to give the entire parameter an empty-object default:
function summarizeSafe({ station = "?", temperature = null } = {}) { return `${station}: ${temperature}`; } console.log(summarizeSafe()); console.log(summarizeSafe({ station: "B1" }));
?: null B1: null
Two layers of defaults work together: the outer = {} kicks in if the object itself
is missing, the inner ones kick in if individual fields are missing.
If all of a function’s options are required, the outer default is not written; then a missing call throws an error, and this is the intended behavior. The choice depends on what the contract is.
Summary
- A missing argument becomes
undefined, an extra argument is ignored; neither produces an error. - A default value kicks in only when the argument is
undefined;nulland0do not trigger the default. - Default expressions are evaluated on every call and only when needed; they can use the parameters before them.
- A rest parameter collects arguments into a real array and must be last; three dots at a call site work in reverse, spreading an array into arguments.
- The
argumentsobject is not an array and does not count parameters filled by a default; this course uses rest parameters. - A destructured parameter provides named options; if the whole object has no default, a call with no arguments throws an error.
Next Step
Short forms like (d) => d > threshold were used unexplained in this lesson. The next
lesson covers arrow functions: the forms of the short syntax, the implicit return rule,
and the difference these functions have from the arguments object and from context
behavior. The difference is not just brevity — an arrow function cannot be used in some
places.
To keep your progress and take notes, Log in
My notes
Log in to take notes.