Chapter 8: Default Parameters & Named Arguments
Prerequisites & Mental Check:
-
Chapter 1 & 2 Fundamentals: Function execution phases, arrow function syntax, and expression evaluation in memory.
-
Chapter 4 Concepts: Object destructuring and parameter destructuring in function signatures.
-
Chapter 6 Concepts: The 8 falsy values and why logical OR (
||) causes silent bugs when handling0,"", orfalse.
πΊ Video Explanation
Watch the full course with certification on : Udemy
As React Native applications scale, helper utilities, custom hooks, and UI components accept increasingly complex sets of arguments.
In vanilla JavaScript, passing multiple positional arguments is rigid and error-prone: you must remember the exact order, handle omitted parameters defensively, and avoid overriding valid falsy inputs. Combining ES6 Default Parameters with Object Destructuring solves this by enabling clean, self-documenting "named arguments" with built-in fallbacks.
1. The Problem: The Fragile Logical OR (||) Fallback
Before ES6, handling optional parameters required manual fallback assignments inside the function body:
function greet(name) {
name = name || "Athlete";
console.log(`Hello, ${name}!`);
}
As established in [Chapter 6: Short-Circuit Evaluation & Optional Chaining], the logical OR operator (||) triggers on any falsy value (0, "", false, null, undefined, NaN). This creates silent bugs when 0 or empty strings represent valid input:
function setVolume(level) {
level = level || 50; // β Overrides 0 with 50!
console.log(`Volume: ${level}`);
}
setVolume(0); // Outputs: "Volume: 50" (Unexpected!)
2. ES6 Default Parameters: The Exact Evaluation Rule
Default parameters allow you to declare fallback values directly within the function parameter list:
function greet(name = "Athlete") {
console.log(`Hello, ${name}!`);
}
greet(); // "Hello, Athlete!" (No argument passed -> undefined)
greet("Subrata"); // "Hello, Subrata!"
greet(undefined); // "Hello, Athlete!" (undefined triggers the default)
greet(null); // "Hello, null!" (null does NOT trigger the default)
The Golden Trigger Rule
$$\text{Default Parameter Triggered} \iff \text{Passed Value} === \text{undefined}$$
Default parameters trigger only when the argument is undefined (either omitted or passed explicitly). They preserve valid falsy inputs like 0, "", and false:
function setVolume(level = 50) {
console.log(`Volume: ${level}`);
}
setVolume(0); // Outputs: "Volume: 0" β
Preserved correctly
3. Advanced Mechanics: Dynamic & Dependent Defaults
1. Defaults Can Reference Earlier Parameters
JavaScript evaluates parameters left to right in the current scope. A later default parameter can dynamically compute its value from an earlier parameter:
function createUser(name, displayName = name) {
return { name, displayName };
}
console.log(createUser("Subrata"));
// { name: "Subrata", displayName: "Subrata" }
console.log(createUser("Subrata", "Subrata Kumar"));
// { name: "Subrata", displayName: "Subrata Kumar" }
2. Runtime Evaluation (Call-Time Execution)
Default parameter expressions are evaluated at runtime when the function is invoked, not when the script is parsed:
let counter = 0;
function logCall(id = counter++) {
console.log(`Call ID: ${id}`);
}
logCall(); // Call ID: 0
logCall(); // Call ID: 1
logCall(); // Call ID: 2
Because defaults evaluate at call time, functions like date = new Date().toISOString() produce the exact timestamp of the call rather than the time the module was imported.
4. "Named Arguments" via Destructuring
Languages like Python and Kotlin support native named arguments (createUser(name="Alex", role="Admin")). JavaScript does not have native named parametersβcalling createUser("Alex", "Admin", true) requires you to memorize the exact positional order.
The JavaScript Solution: Destructured Configuration Objects
By accepting a single configuration object and destructuring it in the parameter list (using the principles from [Chapter 4: Object & Array Destructuring]), you simulate named arguments with default fallbacks:
// β Positional: Order-dependent, unclear at call-site
function createUser(name, role, isActive) { / ... / }
createUser("Subrata", "admin", true);
// β
Named Destructuring: Order-independent, self-documenting
function createUser({ name, role = "member", isActive = true }) {
return { name, role, isActive };
}
createUser({ name: "Subrata", role: "admin" });
// { name: "Subrata", role: "admin", isActive: true }
Positional Parameters(Rigid)
createUser("Subrata", "admin", true)
β β β
βΌ βΌ βΌ
(Param 1: name, Param 2: role, Param 3: isActive)
Named Object Destructuring(Flexible)
createUser({ role: "admin", name: "Subrata" })
β β
βΌ βΌ
(Extracted by Key: role) (Extracted by Key: name)
5. The = {} Safety Net Pattern
When destructuring an object parameter, a missing argument will cause a runtime crash if you omit the fallback wrapper = {}:
// β CRASHES if called without arguments
const useDebounce = (value, { delay = 300 }) => { / ... / };
useDebounce("search");
// π₯ TypeError: Cannot destructure property 'delay' of 'undefined'
Why does this happen?
When the second argument is omitted, JavaScript passes undefined. The engine attempts to unpack { delay } from undefined, causing a runtime TypeError.
The Two-Level Fallback Solution:
// β
Safe: Defaults the entire parameter to {}, then unpacks delay
const useDebounce = (value, { delay = 300, immediate = false } = {}) => {
console.log(`Delay: ${delay}, Immediate: ${immediate}`);
};
useDebounce("search", { delay: 500 }); // Delay: 500, Immediate: false
useDebounce("search"); // Delay: 300, Immediate: false β
Works safely!
$$\begin{aligned} \textbf{Outer Fallback (} = {} \textbf{)} &\longrightarrow \text{Protects against missing parameter argument (\texttt{undefined})} \ \textbf{Inner Fallback (} \text{key} = \text{val} \textbf{)} &\longrightarrow \text{Protects against missing property on passed object} \end{aligned}$$
6. Real-World Use Cases in React Native
1. Reusable UI Components with Computed Style Defaults
import React from 'react';
import { Image, StyleSheet } from 'react-native';
const Avatar = ({
uri,
size = 48,
borderRadius = size / 2, // Dependent default parameter!
borderColor = "#E2E8F0"
}) => (
<Image
source={{ uri }}
style={{
width: size,
height: size,
borderRadius,
borderWidth: 2,
borderColor
}}
/>
);
// Call sites are clear and self-documenting:
<Avatar uri="https://example.com/user.png" size={64} />
2. Flexible API Service Helpers
const BASE_URL = "https://api.healthapp.com";
const fetchSteps = async ({
userId,
startDate,
endDate = new Date().toISOString() // Dynamic call-time default
}) => {
const response = await fetch(
`${BASE_URL}/users/${userId}/steps?from=${startDate}&to=${endDate}`
);
return response.json();
};
// Caller omits endDate safely:
fetchSteps({ userId: "u_123", startDate: "2026-06-01" });
3. Custom Hook Configuration Wrappers
const useStepTracker = ({
targetGoal = 10000,
autoSync = true,
intervalMs = 5000
} = {}) => {
// Hook logic implementation
};
// Safe invocation with zero config:
useStepTracker();
7. Architectural Comparison: Argument Passing Strategies
| Strategy | Syntax | Call-Site Clarity | Order Dependent? | Safety on Missing Args |
|---|---|---|---|---|
| Positional Parameters | fn(a, b, c) |
Low (unclear with 3+ args) | Strict Order | Requires trailing defaults |
| Manual OR Check | `a = a | def` | Medium | |
| ES6 Default Parameters | fn(a = 10) |
Medium | Strict Order | β
Triggers only on undefined |
Destructured Object (= {}) |
fn({ a = 10 } = {}) |
High (Named Args) | Independent | β Bulletproof at all levels |
The Decision Rule
Number of function arguments?
βββ 1 to 2 parameters βββΊ Standard positional parameters with default values
βββ 3 or more parameters βββΊ Destructured configuration object with `{ ... } = {}`
8. Common Pitfalls & Mistakes
Pitfall 1: Using || Fallbacks for Numerical Properties
// β WRONG: Passing 0 forces opacity to 1.0
function renderOverlay(opacity) {
const finalOpacity = opacity || 1.0;
}
// β
CORRECT: Preserves 0 as a valid transparency level
function renderOverlay(opacity = 1.0) {
// opacity === 0 is respected
}
Pitfall 2: Omitting the Outer = {} on Destructured Configs
// β CRASHES if user passes no arguments
const initLogger = ({ prefix = "[APP]", level = "info" }) => {};
initLogger(); // TypeError: Cannot destructure property of undefined
// β
SAFE: Works with or without arguments passed
const initLogger = ({ prefix = "[APP]", level = "info" } = {}) => {};
initLogger(); // prefix: "[APP]", level: "info"
Pitfall 3: Expecting null to Trigger Default Values
function loadTheme(theme = "dark") {
console.log(theme);
}
loadTheme(null); // Logs: null (NOT "dark"!)
Remember: null represents an explicit value; defaults trigger only on undefined.
Technical Interview Questions
Q1: What will be logged to the console by the following code?
function compute(a = 10, b = 20, c = a + b) {
console.log(a, b, c);
}
compute(undefined, 5);
compute(null, 5);
- Output:
10 5 15
null 5 5
- Explanation: In the first call,
areceivesundefinedwhich triggers its default (10).bis passed as5.cis omitted (undefined), so it evaluatesa + b($10 + 5 = 15$). In the second call,aisnull, which is an explicit value that prevents the default from triggering (a = null).bis5.cevaluatesnull + 5, coercingnullto0, resulting in5.
Q2: Will the following function throw an error when executed? Why or why not?
function configure(width = height, height = 100) {
return { width, height };
}
configure(undefined, 200);
- Output: Throws
ReferenceError: Cannot access 'height' before initialization. - Explanation: Default parameters evaluate strictly left-to-right within the parameter scope. When
widthevaluates its default (height), the identifierheightis still in the Temporal Dead Zone (TDZ) because its declaration has not yet been reached in the parameter list.
Q3: Convert the following multi-argument function into a safe "named arguments" signature with default values:
function createNotification(title, message, isSticky, timeoutMs, priority) {
// implementation
}
- Answer:
function createNotification({
title,
message = "",
isSticky = false,
timeoutMs = 4000,
priority = "normal"
} = {}) {
return { title, message, isSticky, timeoutMs, priority };
}
Q4: How do default parameters affect the .length property of a function in JavaScript?
- Answer: A function's
.lengthproperty returns the number of expected positional arguments. It counts arguments up to (but not including) the first parameter that has a default value or uses rest syntax (...args):
function fn1(a, b, c) {} // fn1.length === 3
function fn2(a, b = 10, c) {} // fn2.length === 1
function fn3({ a, b } = {}) {} // fn3.length === 0
Q5: Why does ({ data = "Empty" } = {}) require two separate assignments of = , and what role does each play?
- Answer:
-
The inner
= "Empty"provides a fallback for the propertydatawhen an object is passed but does not contain adatakey (e.g.,fn({})$\rightarrow$data = "Empty"). -
The outer
= {}provides a fallback for the entire argument when no argument is supplied at all (e.g.,fn()$\rightarrow$ argument defaults to{}, which then defaultsdatato"Empty"). Omitting the outer fallback causes aTypeErrorwhen reading properties fromundefined.