Default Parameters
ES6 introduced default parameters, allowing you to specify default values for function parameters. This feature eliminates the need for manual parameter checking and makes your functions more robust and easier to use.
Basic Default Parameters
Before ES6, you had to manually check for undefined parameters. Now you can set defaults directly in the function signature:
// ES5 - Manual parameter checking
function greet(name) {
name = name || 'Guest';
return 'Hello, ' + name;
}
// ES6+ - Default parameters
function greetES6(name = 'Guest') {
return `Hello, ${name}`;
}
console.log(greetES6()); // "Hello, Guest"
console.log(greetES6('Alice')); // "Hello, Alice"
// Multiple default parameters
function createUser(name = 'Anonymous', role = 'user', active = true) {
return { name, role, active };
}
console.log(createUser());
// { name: 'Anonymous', role: 'user', active: true }
console.log(createUser('Bob', 'admin'));
// { name: 'Bob', role: 'admin', active: true }
Tip: Default parameters are only used when the argument is undefined. Passing null, 0, false, or '' will NOT trigger the default value.
Expression-Based Defaults
Default values can be any expression, not just literals. They're evaluated at call time:
// Function call as default
function getDefaultName() {
console.log('Getting default name...');
return 'Guest';
}
function greet(name = getDefaultName()) {
return `Hello, ${name}`;
}
greet('Alice'); // No console output - default not used
greet(); // Logs: "Getting default name..." then returns "Hello, Guest"
// Expression as default
function calculatePrice(price, tax = price * 0.1) {
return price + tax;
}
console.log(calculatePrice(100)); // 110 (100 + 10)
console.log(calculatePrice(100, 5)); // 105 (100 + 5)
// Date as default
function logMessage(message, timestamp = Date.now()) {
console.log(`[${timestamp}] ${message}`);
}
Important: Default value expressions are evaluated every time the function is called and the parameter is undefined, not once when the function is defined.
Using Previous Parameters in Defaults
Later parameters can reference earlier parameters in their default values:
// Using earlier parameter in later default
function createFullName(firstName = 'John', lastName = firstName + 'son') {
return `${firstName} ${lastName}`;
}
console.log(createFullName()); // "John Johnson"
console.log(createFullName('Bob')); // "Bob Bobson"
console.log(createFullName('Alice', 'Smith')); // "Alice Smith"
// More complex example
function createRange(start = 0, end = start + 10, step = 1) {
const range = [];
for (let i = start; i < end; i += step) {
range.push(i);
}
return range;
}
console.log(createRange()); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(createRange(5)); // [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
console.log(createRange(0, 5)); // [0, 1, 2, 3, 4]
console.log(createRange(0, 10, 2)); // [0, 2, 4, 6, 8]
Important: You can only reference parameters that appear earlier in the parameter list. Later parameters are in the "temporal dead zone" and cannot be accessed.
undefined vs null Handling
Understanding how default parameters handle different falsy values is crucial:
function test(value = 'default') {
return value;
}
// undefined triggers default
console.log(test(undefined)); // "default"
console.log(test()); // "default"
// Other falsy values do NOT trigger default
console.log(test(null)); // null
console.log(test(0)); // 0
console.log(test(false)); // false
console.log(test('')); // ""
console.log(test(NaN)); // NaN
// Practical handling of null
function greet(name = 'Guest') {
// If you want null to also use default
name = name ?? 'Guest'; // Using nullish coalescing
return `Hello, ${name}`;
}
Default Parameters with Destructuring
Default parameters work beautifully with destructuring for flexible function APIs:
// Object parameter with defaults
function createButton({
text = 'Click me',
color = 'blue',
size = 'medium',
disabled = false
} = {}) {
return { text, color, size, disabled };
}
console.log(createButton());
// { text: 'Click me', color: 'blue', size: 'medium', disabled: false }
console.log(createButton({ text: 'Submit', color: 'green' }));
// { text: 'Submit', color: 'green', size: 'medium', disabled: false }
// Note the = {} at the end - allows calling with no arguments
console.log(createButton()); // Works!
// Array destructuring with defaults
function processCoordinates([x = 0, y = 0, z = 0] = []) {
return { x, y, z };
}
console.log(processCoordinates()); // { x: 0, y: 0, z: 0 }
console.log(processCoordinates([10, 20])); // { x: 10, y: 20, z: 0 }
Best Practice: When using object destructuring in parameters, always add = {} as a default for the entire parameter. This allows the function to be called without any arguments.
Migration from ES5 Patterns
// ES5 - Multiple patterns for defaults
function oldWay(name, role, active) {
// Pattern 1: OR operator (has issues with falsy values)
name = name || 'Guest';
// Pattern 2: Ternary operator
role = (role !== undefined) ? role : 'user';
// Pattern 3: typeof check
active = (typeof active !== 'undefined') ? active : true;
return { name, role, active };
}
// ES6+ - Clean and clear
function newWay(name = 'Guest', role = 'user', active = true) {
return { name, role, active };
}
// Both produce same result but ES6+ is much cleaner
console.log(newWay('Alice', undefined, false));
// { name: 'Alice', role: 'user', active: false }
Practical Use Cases
// 1. API request configuration
function fetchData(
url,
method = 'GET',
headers = { 'Content-Type': 'application/json' },
timeout = 5000
) {
return fetch(url, {
method,
headers,
signal: AbortSignal.timeout(timeout)
});
}
// 2. Pagination
function getPaginatedData(
page = 1,
limit = 10,
sortBy = 'createdAt',
order = 'desc'
) {
const offset = (page - 1) * limit;
return {
query: `SELECT * FROM items ORDER BY ${sortBy} ${order} LIMIT ${limit} OFFSET ${offset}`,
page,
limit
};
}
console.log(getPaginatedData());
// { query: "SELECT * FROM items ORDER BY createdAt desc LIMIT 10 OFFSET 0", page: 1, limit: 10 }
// 3. Logger with severity levels
function log(
message,
level = 'info',
timestamp = new Date().toISOString()
) {
console.log(`[${timestamp}] [${level.toUpperCase()}] ${message}`);
}
log('Application started');
// [2024-02-08T10:30:00.000Z] [INFO] Application started
// 4. Configuration merger
function createConfig(
env = 'development',
port = env === 'production' ? 80 : 3000,
debug = env !== 'production'
) {
return { env, port, debug };
}
console.log(createConfig());
// { env: 'development', port: 3000, debug: true }
console.log(createConfig('production'));
// { env: 'production', port: 80, debug: false }
Complex Default Patterns
// Nested defaults with destructuring
function createNotification({
title = 'Notification',
message = 'You have a new message',
options: {
duration = 3000,
position = 'top-right',
closable = true
} = {}
} = {}) {
return {
title,
message,
options: { duration, position, closable }
};
}
console.log(createNotification());
// Full defaults applied
console.log(createNotification({
title: 'Alert',
options: { duration: 5000 }
}));
// { title: 'Alert', message: 'You have a new message',
// options: { duration: 5000, position: 'top-right', closable: true } }
// Factory with incremental IDs
let lastId = 0;
function createItem(name, id = ++lastId) {
return { id, name };
}
console.log(createItem('Item 1')); // { id: 1, name: 'Item 1' }
console.log(createItem('Item 2')); // { id: 2, name: 'Item 2' }
console.log(createItem('Custom', 100)); // { id: 100, name: 'Custom' }
console.log(createItem('Item 3')); // { id: 3, name: 'Item 3' }
Required Parameters Pattern
// Creating a "required parameter" by throwing in default
function required(paramName) {
throw new Error(`Parameter "${paramName}" is required`);
}
function createUser(
name = required('name'),
email = required('email'),
role = 'user'
) {
return { name, email, role };
}
// This works
console.log(createUser('Alice', 'alice@example.com'));
// { name: 'Alice', email: 'alice@example.com', role: 'user' }
// This throws an error
try {
createUser('Bob'); // Error: Parameter "email" is required
} catch (error) {
console.error(error.message);
}
Practice Exercise:
Task: Create a formatCurrency function with default parameters:
- Accept
amount, currency (default: 'USD'), locale (default: 'en-US'), and decimals (default: 2)
- Use the
Intl.NumberFormat API to format the currency
- Make
decimals depend on the currency (some currencies don't use decimals)
Solution:
function formatCurrency(
amount = 0,
currency = 'USD',
locale = 'en-US',
decimals = ['JPY', 'KRW'].includes(currency) ? 0 : 2
) {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency,
minimumFractionDigits: decimals,
maximumFractionDigits: decimals
}).format(amount);
}
console.log(formatCurrency(1234.56)); // "$1,234.56"
console.log(formatCurrency(1234.56, 'EUR', 'de-DE')); // "1.234,56 €"
console.log(formatCurrency(1234.56, 'JPY', 'ja-JP')); // "¥1,235"
console.log(formatCurrency()); // "$0.00"
Performance Considerations
Note: Default parameter expressions are evaluated each time the function is called. Avoid expensive operations in defaults. If you need complex initialization, consider doing it inside the function body instead.
Common Pitfalls
// ❌ Don't use OR operator for defaults anymore
function bad(count) {
count = count || 10; // Problem: 0 will become 10!
}
bad(0); // count becomes 10 (wrong!)
// ✅ Use default parameters
function good(count = 10) {
return count;
}
good(0); // count is 0 (correct!)
// ❌ Don't forget the = {} for object destructuring
function bad({ name, age }) {
return { name, age };
}
// bad(); // Error: Cannot destructure undefined
// ✅ Add default empty object
function good({ name = 'Guest', age = 0 } = {}) {
return { name, age };
}
good(); // Works! Returns { name: 'Guest', age: 0 }
Summary
In this lesson, you learned:
- Default parameters simplify function definitions and eliminate manual checking
- Defaults are only used when arguments are
undefined, not for other falsy values
- Default values can be expressions evaluated at call time
- Later parameters can reference earlier parameters in their defaults
- Combine defaults with destructuring for flexible function APIs
- Use the required parameter pattern to enforce mandatory arguments
Next Up: In the next lesson, we'll explore Rest Parameters and Arguments for handling variable numbers of function parameters!