SECTION 1
Historical Context & Motivation
Before 2015, JavaScript developers faced a persistent annoyance: functions had no built-in way to assign fallback values to parameters. If you called a function without supplying every argument, those missing parameters silently became undefined, which frequently caused bugs. Developers relied on clunky workarounds like x = x || 5 inside the function body, but this pattern had its own problems — it would replace legitimate values like 0 or false with the fallback too.
Similarly, handling a variable number of arguments was awkward. JavaScript provided a hidden arguments object inside every function, but it was not a true array. You couldn't use methods like .map() or .filter() on it directly, which meant extra conversion steps and confusing code.
1995 • JavaScript Is Born
1999 • ES3 Standardizes the arguments Object
arguments object — an array-like structure available inside every function. Developers use it to handle variable arguments, but its quirks cause many headaches.2009 • CoffeeScript Inspires Change
2015 • ES6 (ES2015) Arrives
...args). These features are now standard in all modern browsers and Node.js.The central question ES6 answered was straightforward: How can we make functions more flexible and readable when dealing with missing or variable-length arguments? Default and rest parameters were the elegant solution.
SECTION 2
Core Principles & Definitions
Default and rest parameters both solve the same broad problem — making functions more adaptable — but they attack it from different angles. Understanding both features together will make your functions shorter, clearer, and less prone to errors.
Default Parameters
undefined), the default value is used instead.Rest Parameters
... syntax to collect all remaining arguments into a real JavaScript array. It must be the last parameter in the function signature.undefined vs. Other Falsy Values
undefined. Passing null, 0, or "" will NOT trigger the default — the passed value is used as-is.True Array, Not arguments
arguments object, a rest parameter is a genuine Array. You can immediately use .map(), .reduce(), .filter(), and any other array method.KEY TAKEAWAY
SECTION 3
Visual Explanation
undefined, so the default value "World" is used. The remaining arguments are gathered by the rest parameter into a real array.Notice two key details in the diagram above. First, the default parameter only activates when the caller passes undefined (or omits the argument entirely). If we had passed an empty string "", the name would have been the empty string — not "World". Second, the rest parameter ...titles creates a clean array, which means you can chain array methods on it right away.
SECTION 4
How It Works — Syntax Deep Dive
Default Parameter Syntax
To set a default parameter, you use the assignment operator = directly in the function signature. This tells JavaScript: "If this argument is not provided or is undefined, use this fallback instead."
DEFAULT PARAMETER PATTERN
Each parameter can have its own default. Defaults are evaluated left to right, so a later default can reference an earlier parameter: function area(w, h = w) { return w * h; }
Rest Parameter Syntax
The rest parameter uses three dots ... before a parameter name. It gathers all remaining arguments into a real array. There are two hard rules: (1) there can be only one rest parameter per function, and (2) it must be the last parameter in the list.
REST PARAMETER PATTERN
If the caller passes 5 arguments and there are 2 named parameters, restArray will contain the remaining 3 values as an Array of length 3.
Combining Both Features
You can use default parameters and rest parameters in the same function. The defaults come first, and the rest parameter is always last. For example, function log(level = "INFO", ...messages) gives level a default of "INFO" and collects all remaining arguments into the messages array. This pattern is extremely common in real-world codebases.
Common Pitfall
...args = [] is a syntax error. However, if no extra arguments are passed, the rest parameter is simply an empty array [], so you rarely need a default for it anyway.SECTION 5
Old Way vs. New Way — Side-by-Side
The best way to appreciate default and rest parameters is to compare ES5 (the old way) and ES6 (the modern way) side by side. The diagram below shows identical logic written both ways.
| Feature | ES5 (Old Way) | ES6 (Modern Way) |
|---|---|---|
| Setting defaults | x = x || fallback | function fn(x = fallback) |
| Handles 0 / "" correctly? | ❌ No — treats them as missing | ✅ Yes — only undefined triggers default |
| Gathering extra args | Array.from(arguments) | function fn(...args) |
| Result is a real Array? | ❌ No — must convert | ✅ Yes — it's already an Array |
| Works in arrow functions? | ❌ arguments not available | ✅ Yes — rest works everywhere |
Key differences between ES5 workarounds and ES6 parameter features
SECTION 6
Worked Example — Building a Flexible Logger
Let's build a real-world utility function that uses both default and rest parameters. We'll create a log function that accepts an optional severity level and any number of messages to print.
Building a Flexible Logger Function
- 1
Step 1 — Define the Function Signature
We want a severity level that defaults to"INFO"if not provided, followed by any number of message strings. The rest parameter must come last.function log(level = "INFO", ...messages) - 2
Step 2 — Build the Function Body
Inside the function, we create a timestamp string, then join all messages with a space. Sincemessagesis a real array, we can call.join(" ")directly on it.const timestamp = new Date().toISOString();const output = messages.join(" "); - 3
Step 3 — Format and Print the Output
We use a template literal to combine the timestamp, level, and joined messages into a formatted log string, then print it withconsole.log().console.log(`[${timestamp}] [${level}] ${output}`); - 4
Step 4 — Test with Various Calls
Callinglog("ERROR", "File", "not", "found")sets level to "ERROR" and collects the three remaining strings. Callinglog(undefined, "Server", "started")triggers the default, so level becomes "INFO".Output 1:[2025-01-15T...] [ERROR] File not found| Output 2:[2025-01-15T...] [INFO] Server started - 5
Step 5 — Complete Function
Here is the full function combining all the pieces. Notice how clean it is compared to the ES5 approach — no manual argument checking, no array conversion, and the signature itself documents the expected inputs.function log(level = "INFO", ...messages) { const timestamp = new Date().toISOString(); console.log(`[${timestamp}] [${level}] ${messages.join(" ")}`); }
SECTION 7
Strengths, Limitations & Best Practices
| Feature | Strengths | Limitations / Gotchas |
|---|---|---|
| Default Parameters | Cleaner code; self-documenting signatures; correctly handles 0, "", and false | Defaults are evaluated at call time (not declaration), so using mutable objects like arrays or objects as defaults creates a new one each call — which is usually fine, but be aware of it. |
| Rest Parameters | Produces a real Array; works in arrow functions; clearly communicates intent in the signature. | Must be the last parameter. Only one allowed per function. Cannot have a default value assigned to it. |
| Combined Use | Extremely expressive — a single function can handle many calling patterns without conditional logic. | If overused, functions can become ambiguous. Keep parameter lists under 4–5 items; use an options object for more. |
Strengths and limitations of default and rest parameters
BEST PRACTICE TIP
undefined. For example, function createUser(name, role = "viewer") lets you call createUser("Alice") cleanly.SECTION 8
Connection to Advanced Patterns
Default and rest parameters are foundational features that connect to several more advanced JavaScript patterns you'll encounter as you progress. Understanding them now sets you up for destructuring, spread syntax, and functional programming techniques.
| This Lesson's Concept | Advanced Connection | How They Relate |
|---|---|---|
| Default parameters | Destructuring with defaults | You can combine destructuring and defaults: function draw({ color = "red", size = 10 } = {}) {}. This is the standard pattern for options objects. |
| Rest parameters | Spread syntax | The ... operator does double duty: in a parameter list it gathers (rest), in a function call it spreads. Math.max(...myArray) uses spread to expand an array into arguments. |
| Rest parameters | Higher-order functions | Rest parameters are essential for writing wrapper functions, decorators, and middleware — patterns where a function passes all its arguments through to another function. |
| Both features | TypeScript | TypeScript adds static types to default and rest parameters. You'll write function sum(...nums: number[]): number for type-safe variadic functions. |
How default and rest parameters connect to advanced JavaScript patterns
As you advance, you'll see these features everywhere — in React component props, Express.js route handlers, and utility libraries. Mastering them now gives you a solid foundation for writing clean, professional JavaScript.
SECTION 9
Practice Problems
Question 1: CONCEPTUAL
function greet(name = "friend") { return "Hi, " + name; }. What does greet(null) return? Explain why.Show answer
"Hi, null". Default parameters only activate when the argument is undefined (or omitted entirely). Since null is a deliberately passed value — even though it's falsy — JavaScript does not replace it with the default. The null value is then concatenated with the string, producing "Hi, null".Question 2: BASIC CALCULATION
multiply(a, b = 1) that returns the product of a and b. What does multiply(7) return? What does multiply(7, 0) return?Show answer
function multiply(a, b = 1) { return a * b; }. Calling multiply(7) returns 7 because b uses its default of 1, and 7 × 1 = 7. Calling multiply(7, 0) returns 0 because 0 is not undefined — the default is not triggered, so 7 × 0 = 0.Question 3: INTERMEDIATE
average(...numbers) that accepts any number of numeric arguments and returns their arithmetic mean. If no arguments are passed, it should return 0. Test it with average(10, 20, 30) and average().Show answer
function average(...numbers) { if (numbers.length === 0) return 0; return numbers.reduce((sum, n) => sum + n, 0) / numbers.length; }. Since numbers is a real array (thanks to rest parameters), we use .reduce() to sum all values, then divide by the array's length. average(10, 20, 30) returns 20 (sum is 60, divided by 3). average() returns 0 because the guard clause catches the empty array case.Question 4: APPLIED
calculateTotal(taxRate = 0.08, ...prices) that sums all the prices and applies the given tax rate. For example, calculateTotal(0.10, 25, 15, 30) should return 77 (70 + 10% tax). What does calculateTotal(undefined, 50, 50) return?Show answer
function calculateTotal(taxRate = 0.08, ...prices) { const subtotal = prices.reduce((sum, p) => sum + p, 0); return subtotal + subtotal * taxRate; }. For calculateTotal(0.10, 25, 15, 30): the subtotal is 70, tax is 70 × 0.10 = 7, so the total is 77. For calculateTotal(undefined, 50, 50): passing undefined triggers the default tax rate of 0.08. The subtotal is 100, tax is 100 × 0.08 = 8, so the result is 108.Question 5: CRITICAL THINKING
function createTag(tag = "div", ...classes, id) { /* ... */ }. Before running the code, predict what will happen. Then redesign the function signature so it correctly accepts a tag name (default "div"), any number of CSS class names, and an optional id.Show answer
...classes is not the last parameter — id comes after it. A rest parameter must always be the final parameter. One clean redesign uses an options object: function createTag({ tag = "div", id = null, classes = [] } = {}) { /* ... */ }. Alternatively, you could move id before the rest: function createTag(tag = "div", id = null, ...classes). The options-object approach is generally preferred when you have more than 2–3 parameters because it's more readable and order-independent.SUMMARY
Summary — Default & Rest Parameters
Summary
Default parameters let you assign fallback values directly in a function's signature using the = operator. They activate only when the argument is undefined or omitted, which means passing null, 0, or "" will not trigger the default. Later defaults can reference earlier parameters, enabling expressive patterns like function area(w, h = w).
Rest parameters use the ... (three-dot) syntax to collect all remaining arguments into a real JavaScript Array. They must be the last parameter in the function signature, and only one is allowed per function. Together, these two ES6 features replace the old || fallback trick and the clunky arguments object, making your functions shorter, safer, and easier to read.