Advanced JavaScript (ES6+)

Default Parameters

13 min Lesson 8 of 40

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:

  1. Accept amount, currency (default: 'USD'), locale (default: 'en-US'), and decimals (default: 2)
  2. Use the Intl.NumberFormat API to format the currency
  3. 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!

ES
Edrees Salih
15 hours ago

We are still cooking the magic in the way!