Positional Parameters
Parameters are matched to arguments by position. The first argument goes to the first parameter, and so on.
function introduce(firstName, lastName, age) {
return `${firstName} ${lastName}, age ${age}`;
}
console.log(introduce("Alice", "Smith", 30)); // Alice Smith, age 30
console.log(introduce("Bob", "Jones", 25)); // Bob Jones, age 25
// Extra arguments are silently ignored
console.log(introduce("Alice", "Smith", 30, "extra")); // OK
// Missing arguments become undefined
console.log(introduce("Alice")); // Alice undefined, age undefined
Default Parameter Values
Default values are used when an argument is undefined (missing or explicitly passed as undefined).
function createUser(name, role = "viewer", isActive = true) {
return { name, role, isActive };
}
console.log(createUser("Alice")); // {name:'Alice', role:'viewer', isActive:true}
console.log(createUser("Bob", "admin")); // {name:'Bob', role:'admin', isActive:true}
console.log(createUser("Carol", "editor", false)); // {name:'Carol', role:'editor', isActive:false}
// Passing undefined explicitly triggers the default
console.log(createUser("Dave", undefined, false)); // {name:'Dave', role:'viewer', isActive:false}
// Passing null does NOT trigger the default
console.log(createUser("Eve", null, true)); // {name:'Eve', role:null, isActive:true}
Defaults only kick in for undefined, not null. This is because null is an intentional "empty" value, while undefined means "nothing was provided".
Rest Parameters (...args)
The rest parameter syntax ...name collects all remaining arguments into a real Array. It must be the last parameter.
// Variadic sum function
function sum(...numbers) {
return numbers.reduce((acc, n) => acc + n, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(sum()); // 0
// Mix: fixed params + rest
function logMessage(level, ...messages) {
const formatted = messages.join(" ");
console.log(`[${level.toUpperCase()}] ${formatted}`);
}
logMessage("info", "Server", "started", "on port 3000");
logMessage("error", "Connection", "refused");
The arguments Object (Legacy)
Before rest parameters existed, arguments was used to access all passed arguments. It's array-like but not a real Array, and it's unavailable in arrow functions.
// Legacy approach β avoid in modern code
function legacySum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(legacySum(1, 2, 3)); // 6
// Modern rest parameter approach (preferred)
function modernSum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
console.log(modernSum(1, 2, 3)); // 6
Destructured Parameters
You can destructure an object or array directly in the parameter list, extracting values by name.
// Object destructuring in parameters
function displayUser({ name, age, city = "Unknown" }) {
console.log(`${name}, ${age} years old, from ${city}`);
}
displayUser({ name: "Alice", age: 30, city: "London" });
displayUser({ name: "Bob", age: 25 }); // city defaults to "Unknown"
// Array destructuring in parameters
function processCoords([x, y, z = 0]) {
console.log(`x=${x}, y=${y}, z=${z}`);
}
processCoords([10, 20]);
processCoords([1, 2, 3]);
Named Parameters Pattern
When a function needs many optional configuration options, pass a single options object. This is the "named parameters" pattern β much cleaner than many positional arguments.
// Hard to read: what does each argument mean?
function createButton("Submit", true, "primary", "lg", null) { /* ... */ }
// Named parameters β self-documenting
function createButton({ text, disabled = false, variant = "default", size = "md", icon = null } = {}) {
return { text, disabled, variant, size, icon };
}
const btn = createButton({ text: "Submit", variant: "primary", size: "lg" });
console.log(btn);
// Can call with no args because of = {} default
const defaultBtn = createButton();
console.log(defaultBtn);
Pass by Value vs Pass by Reference
Primitives (numbers, strings, booleans) are passed by value β a copy is made. Objects and arrays are passed by reference β the function gets access to the original.
// Primitive β pass by value (copy)
function double(n) {
n = n * 2; // Modifies local copy only
return n;
}
let x = 5;
double(x);
console.log(x); // Still 5 β original unchanged
// Object β pass by reference
function addScore(player) {
player.score += 10; // Modifies original!
}
const p = { name: "Alice", score: 100 };
addScore(p);
console.log(p.score); // 110 β original changed!
// Avoid mutation: create and return new object
function addScoreImmutable(player) {
return { ...player, score: player.score + 10 };
}
const p2 = { name: "Bob", score: 100 };
const p2Updated = addScoreImmutable(p2);
console.log(p2.score); // 100 β original safe
console.log(p2Updated.score); // 110
Callbacks as Parameters
// Higher-order function accepting a callback
function processItems(items, transform) {
return items.map(transform);
}
const prices = [10, 20, 30];
const withTax = processItems(prices, price => price * 1.2);
console.log(withTax); // [12, 24, 36]
// Callback with error-first pattern (Node.js style)
function fetchData(url, onSuccess, onError) {
if (!url) {
onError(new Error("URL required"));
return;
}
onSuccess({ data: "mock response from " + url });
}
fetchData("https://api.example.com",
(res) => console.log("Got:", res.data),
(err) => console.log("Error:", err.message)
);
Parameters: Defaults, Rest, and Pass-by-Value-of-Reference
Modern JS parameters are flexible: default values, and a rest parameter that gathers extra arguments into a real array.
function greet(name, greeting = "Hello") { // default
return `${greeting}, ${name}`;
}
function sum(...nums) { // rest β array
return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6
How arguments are passed
JavaScript is always pass-by-value β but for objects, the "value" is a reference. So reassigning a parameter doesn't affect the caller, yet mutating an object parameter does:
function f(x, obj) {
x = 99; // caller's variable unchanged (primitive copy)
obj.a = 99; // caller's object IS changed (same reference)
obj = {}; // reassigning the ref β caller unaffected
}
Two more notes: the old arguments object still exists in regular functions but not in arrow functions β use rest (...args) instead, which gives a real array (arguments is array-like). And JS never enforces arity: extra arguments are ignored, missing ones are undefined, so default parameters are how you guard against undefined.
ποΈ Practical Exercise
Write a function formatCurrency(amount, { currency = 'USD', decimals = 2, symbol = '$' } = {}) using the named parameters pattern. It should return a string like "$1,234.56". Test it with:
formatCurrency(1234.5)formatCurrency(1000, { currency: 'EUR', symbol: 'β¬' })formatCurrency(500, { decimals: 0 })
π₯ Challenge Exercise
Build a function pipeline(...fns) that accepts any number of functions as rest parameters and returns a new function. The returned function passes its input through all the provided functions in order (output of one becomes input of the next). Test with: pipeline(x => x * 2, x => x + 1, x => x ** 2)(3) β should produce 49.
π Summary
- Parameters are positional; extra arguments are ignored, missing ones are
undefined. - Default values fill in for
undefined(notnull). - Rest parameters (
...args) collect remaining arguments into a real Array. - Avoid the legacy
argumentsobject β use rest params instead. - Destructured parameters let you pull values by name directly in the signature.
- Named parameters (options object) are cleaner than many positional args.
- Primitives are passed by value; objects/arrays are passed by reference.
Interview Questions
- What is the difference between rest parameters and the
argumentsobject? - When does a default parameter value trigger? Does
nulltrigger a default? - Explain pass-by-value vs pass-by-reference with examples.
- What is the named parameters pattern and why is it useful?
- How do you handle optional parameters in a clean way?
Related Topics
FAQ
Technically yes, but it's unhelpful. To use the default, you must pass undefined explicitly: fn(undefined, "value"). Always place parameters with defaults after required ones.
Yes. Rest parameters work in all function types including arrow functions. However, the arguments object is NOT available in arrow functions β another reason to prefer rest params.
Arity is the number of parameters a function expects. You can check it with fn.length. Rest parameters and parameters with defaults don't count toward the length property.

