Episode 1 — Fundamentals / 1.20 — Functions
1.20.b — Parameters and Arguments
In one sentence: Parameters are the named placeholders in a function's definition; arguments are the actual values you pass when you call it — and JavaScript gives you default values, rest parameters, destructuring, and the legacy
argumentsobject to build flexible, self-documenting function signatures.
Navigation: ← 1.20.a — Declaration vs Expression · 1.20.c — Return Values and Scope →
1. Parameters vs arguments — the vocabulary
// parameters ↓
function add(a, b) {
return a + b;
}
// arguments ↓
add(3, 5);
| Term | Where | What |
|---|---|---|
| Parameter | Function definition | A named variable that will receive a value |
| Argument | Function call | The actual value passed to a parameter |
Mismatch rules:
- Fewer arguments than parameters — missing params are
undefined. - More arguments than parameters — extras are silently ignored (but accessible via
argumentsor rest).
function demo(a, b) {
console.log(a, b);
}
demo(1); // 1 undefined
demo(1, 2, 3); // 1 2 — the 3 is ignored
2. Default parameters
Introduced in ES6, default parameters provide a fallback when an argument is undefined (or not passed):
function greet(name = "World") {
return `Hello, ${name}!`;
}
console.log(greet()); // "Hello, World!"
console.log(greet("Alice")); // "Hello, Alice!"
console.log(greet(undefined)); // "Hello, World!" — undefined triggers default
console.log(greet(null)); // "Hello, null!" — null does NOT trigger default
Defaults can be expressions
function createId(prefix = "id", num = Date.now()) {
return `${prefix}_${num}`;
}
Defaults can reference earlier parameters
function createElement(tag, className = tag) {
return `<${tag} class="${className}"></${tag}>`;
}
console.log(createElement("div")); // <div class="div"></div>
console.log(createElement("div", "card")); // <div class="card"></div>
Defaults can call functions
function getDefaultName() {
return "Anonymous";
}
function greetUser(name = getDefaultName()) {
console.log(`Welcome, ${name}`);
}
greetUser(); // "Welcome, Anonymous"
greetUser("Bob"); // "Welcome, Bob"
Pre-ES6 pattern (for reference)
// Old style — still seen in legacy code
function greet(name) {
name = name || "World"; // Falsy check — breaks for "", 0, false
name = name !== undefined ? name : "World"; // Safer
return `Hello, ${name}!`;
}
3. Rest parameters (...args)
The rest parameter collects all remaining arguments into a real Array:
function sum(...numbers) {
let total = 0;
for (const n of numbers) {
total += n;
}
return total;
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(10, 20, 30, 40)); // 100
Rules for rest parameters
- Must be the last parameter.
- Only one rest parameter per function.
- Produces a real Array (unlike
arguments).
function logInfo(level, ...messages) {
console.log(`[${level}]`, ...messages);
}
logInfo("ERROR", "File not found", "path: /data");
// [ERROR] File not found path: /data
Rest vs spread — same syntax, different context
// REST — in parameter list: collects into array
function demo(...args) {
console.log(args); // [1, 2, 3]
}
// SPREAD — in function call: expands array into arguments
const nums = [1, 2, 3];
demo(...nums);
4. The arguments object (legacy)
Before rest parameters, JavaScript provided an array-like arguments object inside every (non-arrow) function:
function oldSum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(oldSum(1, 2, 3)); // 6
arguments vs rest parameters
| Feature | arguments | Rest (...args) |
|---|---|---|
| Type | Array-like (no .map, .filter) | Real Array |
| Arrow functions | Not available | Available |
| Named params | Contains all args (named + extra) | Contains only remaining args |
| Modern usage | Legacy — avoid in new code | Preferred |
Converting arguments to a real array
function legacy() {
const args = Array.from(arguments); // or [...arguments]
return args.map(x => x * 2);
}
console.log(legacy(1, 2, 3)); // [2, 4, 6]
5. Passing primitives vs objects
JavaScript is pass-by-value — but for objects, the "value" is a reference (memory address). This leads to behavior that looks like "pass by reference" for objects.
Primitives — passed by value (copy)
function increment(num) {
num = num + 1;
console.log("Inside:", num); // 11
}
let x = 10;
increment(x);
console.log("Outside:", x); // 10 — unchanged
Reassigning num inside the function does not affect the outer x.
Objects — reference is passed by value
function addProperty(obj) {
obj.newProp = "added"; // Mutates the original
}
const user = { name: "Alice" };
addProperty(user);
console.log(user); // { name: "Alice", newProp: "added" } — mutated!
The function receives a copy of the reference — both the outer user and the inner obj point to the same object in memory.
Reassigning the parameter does NOT affect the original
function replace(obj) {
obj = { name: "Bob" }; // Reassigns LOCAL reference
console.log("Inside:", obj); // { name: "Bob" }
}
const user = { name: "Alice" };
replace(user);
console.log("Outside:", user); // { name: "Alice" } — unchanged
Summary table
| What's passed | Mutation inside function | Reassignment inside function |
|---|---|---|
| Primitive (number, string, boolean) | N/A (immutable) | Does not affect outer |
| Object (object, array, function) | Affects outer object | Does not affect outer reference |
Defensive pattern — avoid surprise mutations
function processUser(user) {
const copy = { ...user }; // Shallow copy
copy.processed = true;
return copy;
}
const original = { name: "Alice" };
const result = processUser(original);
console.log(original.processed); // undefined — safe
console.log(result.processed); // true
6. Destructured parameters
You can destructure objects and arrays directly in the parameter list for cleaner, self-documenting code:
Object destructuring
function createUser({ name, age, role = "user" }) {
return { name, age, role, createdAt: Date.now() };
}
const user = createUser({ name: "Alice", age: 30 });
console.log(user);
// { name: "Alice", age: 30, role: "user", createdAt: ... }
With default for the whole parameter (prevents error when called with no argument)
function createUser({ name = "Anon", age = 0, role = "user" } = {}) {
return { name, age, role };
}
console.log(createUser()); // { name: "Anon", age: 0, role: "user" }
console.log(createUser({ name: "Bob" })); // { name: "Bob", age: 0, role: "user" }
Array destructuring
function getFirstAndLast([first, ...rest]) {
const last = rest.length > 0 ? rest[rest.length - 1] : first;
return { first, last };
}
console.log(getFirstAndLast([10, 20, 30])); // { first: 10, last: 30 }
Nested destructuring
function printAddress({ address: { city, zip } }) {
console.log(`${city}, ${zip}`);
}
printAddress({ address: { city: "NYC", zip: "10001" } });
// "NYC, 10001"
7. Parameter validation patterns
Guard clause (early return)
function divide(a, b) {
if (b === 0) {
throw new Error("Cannot divide by zero");
}
return a / b;
}
Type checking
function repeat(str, times) {
if (typeof str !== "string") {
throw new TypeError(`Expected string, got ${typeof str}`);
}
if (typeof times !== "number" || times < 0) {
throw new TypeError("times must be a non-negative number");
}
return str.repeat(times);
}
Required parameter trick
function required(paramName) {
throw new Error(`Missing required parameter: ${paramName}`);
}
function createUser(name = required("name"), email = required("email")) {
return { name, email };
}
createUser("Alice"); // Error: Missing required parameter: email
createUser("Alice", "a@b.com"); // { name: "Alice", email: "a@b.com" }
Config object pattern
When a function needs many options, use an object instead of positional parameters:
// Bad — what do false, true, 3 mean?
createChart("sales", false, true, 3);
// Good — self-documenting
createChart({
title: "sales",
showLegend: false,
animate: true,
columns: 3,
});
function createChart({ title, showLegend = true, animate = false, columns = 1 } = {}) {
console.log(`Chart: ${title}, legend: ${showLegend}, animate: ${animate}, cols: ${columns}`);
}
8. Real examples — flexible function signatures
Example 1: Flexible event logger
function log(message, { level = "info", timestamp = true, prefix = "" } = {}) {
const time = timestamp ? `[${new Date().toISOString()}] ` : "";
const pre = prefix ? `${prefix}: ` : "";
console.log(`${time}${level.toUpperCase()} ${pre}${message}`);
}
log("Server started");
log("Disk full", { level: "error" });
log("Cache hit", { level: "debug", prefix: "CACHE" });
Example 2: Variadic math functions
function min(...values) {
if (values.length === 0) throw new Error("At least one value required");
let result = values[0];
for (const v of values) {
if (v < result) result = v;
}
return result;
}
console.log(min(5, 3, 8, 1)); // 1
// Spread an array into the rest parameter
const scores = [88, 92, 76, 95];
console.log(min(...scores)); // 76
Example 3: Function with mixed required and optional params
function sendEmail(to, subject, { cc = [], bcc = [], html = false } = {}) {
console.log(`To: ${to}`);
console.log(`Subject: ${subject}`);
if (cc.length) console.log(`CC: ${cc.join(", ")}`);
if (bcc.length) console.log(`BCC: ${bcc.join(", ")}`);
console.log(`Format: ${html ? "HTML" : "plain text"}`);
}
sendEmail("alice@example.com", "Hello");
sendEmail("bob@example.com", "Meeting", {
cc: ["carol@example.com"],
html: true,
});
Key takeaways
- Parameters are in the definition; arguments are in the call — extra arguments are ignored, missing ones become
undefined. - Default parameters (
= value) provide fallbacks and can reference earlier params or call functions. - Rest parameters (
...args) collect remaining arguments into a real Array — always the last parameter. - The
argumentsobject is legacy — prefer rest parameters in modern code. - Primitives are copied by value; objects pass a copy of the reference — mutations inside affect the original, reassignments do not.
- Destructured parameters make function signatures self-documenting — always provide
= {}default for optional config objects. - Validate parameters early with guard clauses, type checks, or the required-parameter trick.
Explain-It Challenge
Explain without notes:
- Why does passing a number to a function and reassigning it inside not change the outer variable?
- Why does passing an object and adding a property inside change the outer object?
- What is the difference between
...argsin a parameter list vs...arrin a function call? - Why should you prefer rest parameters over the
argumentsobject?
Navigation: ← 1.20.a — Declaration vs Expression · 1.20.c — Return Values and Scope →