Skip to content

Default Parameters: Giving Function Parameters Default Values

When you order coffee at a coffee shop, the barista might ask: "Would you like sugar?" If you don't answer, some coffee shops will default to no sugar, others will add sugar by default. This "default behavior" makes the ordering process smoother - you don't have to specify every detail each time. In JavaScript, default parameters provide similar convenience, allowing functions to automatically use preset default values when certain parameters are not provided.

What Are Default Parameters

Default Parameters are a feature introduced in ES6 that allows us to specify default values for parameters when declaring functions. If the function is called without providing that parameter, or if the provided value is undefined, the function will use the default value:

javascript
function greet(name = "Guest") {
  console.log(`Hello, ${name}!`);
}

greet("Alice"); // Hello, Alice!
greet(); // Hello, Guest!

In this simple example, if greet() is called without passing a name parameter, it uses the default value "Guest". This is more concise and clear than traditional parameter checking methods.

The Pre-ES6 Approach

Before default parameter syntax was available, we needed to manually check parameters and set default values:

javascript
// Common approach in ES5 and earlier
function greetOldWay(name) {
  // Method 1: Using || operator
  name = name || "Guest";
  console.log("Hello, " + name + "!");
}

// Or
function greetMoreExplicit(name) {
  // Method 2: Explicit undefined check
  if (name === undefined) {
    name = "Guest";
  }
  console.log("Hello, " + name + "!");
}

greetOldWay(); // Hello, Guest!
greetOldWay("Alice"); // Hello, Alice!

While this approach works, it has some issues. When using the || operator, if the passed value is falsy (like 0, "", false), it will also be replaced with the default value:

javascript
function setCount(count) {
  count = count || 10; // Problem: 0 is also treated as falsy
  console.log(`Count: ${count}`);
}

setCount(5); // Count: 5
setCount(0); // Count: 10 (wanted to set to 0, but replaced with default value)
setCount(); // Count: 10

ES6 default parameters only take effect when the parameter is undefined, avoiding this problem:

javascript
function setCount(count = 10) {
  console.log(`Count: ${count}`);
}

setCount(5); // Count: 5
setCount(0); // Count: 0 (correctly handled 0)
setCount(); // Count: 10
setCount(undefined); // Count: 10

How Default Parameters Work

Default parameters only take effect in two situations:

  1. Parameter not passed
  2. Explicitly passed undefined

Passing other falsy values (such as null, 0, false, "") will not trigger the default value:

javascript
function test(value = "default") {
  console.log(value);
}

test(); // "default" - no parameter passed
test(undefined); // "default" - explicitly passed undefined
test(null); // null - null does not trigger default value
test(0); // 0
test(false); // false
test(""); // "" - empty string does not trigger default value

This is an important distinction. undefined means "no value", while null means "has a value, but the value is null". The design of default parameters follows this semantics:

javascript
function createUser(name, age = 18) {
  return {
    name: name,
    age: age,
  };
}

console.log(createUser("Alice"));
// { name: "Alice", age: 18 }

console.log(createUser("Bob", undefined));
// { name: "Bob", age: 18 }

console.log(createUser("Charlie", null));
// { name: "Charlie", age: null } - null does not trigger default value

Multiple Default Parameters

A function can have multiple default parameters, which can be used in any combination:

javascript
function bookFlight(destination, date = "2025-12-31", class = "economy", meal = "standard") {
  console.log(`Booking flight to ${destination}`);
  console.log(`Date: ${date}`);
  console.log(`Class: ${class}`);
  console.log(`Meal: ${meal}`);
}

bookFlight("Tokyo");
// Booking flight to Tokyo
// Date: 2025-12-31
// Class: economy
// Meal: standard

bookFlight("Paris", "2025-06-15");
// Booking flight to Paris
// Date: 2025-06-15
// Class: economy
// Meal: standard

bookFlight("London", "2025-08-01", "business");
// Booking flight to London
// Date: 2025-08-01
// Class: business
// Meal: standard

Skipping Middle Parameters

If you want to use the default value of a certain parameter but set parameters after it, you can explicitly pass undefined:

javascript
function configure(host, port = 8080, protocol = "http", timeout = 5000) {
  console.log(`${protocol}://${host}:${port} (timeout: ${timeout}ms)`);
}

// Want to use port's default value but customize protocol
configure("localhost", undefined, "https");
// https://localhost:8080 (timeout: 5000ms)

// Want to use port and protocol's default values but customize timeout
configure("localhost", undefined, undefined, 10000);
// http://localhost:8080 (timeout: 10000ms)

While this works, multiple undefineds can affect readability. In such cases, using object parameters might be better:

javascript
function configure({ host, port = 8080, protocol = "http", timeout = 5000 }) {
  console.log(`${protocol}://${host}:${port} (timeout: ${timeout}ms)`);
}

configure({
  host: "localhost",
  protocol: "https",
});
// https://localhost:8080 (timeout: 5000ms)

configure({
  host: "localhost",
  timeout: 10000,
});
// http://localhost:8080 (timeout: 10000ms)

Default Parameter Expressions

The value of default parameters can be not just simple literals, but any valid JavaScript expression:

1. Function Call as Default Value

javascript
function getDefaultDate() {
  return new Date().toISOString().split("T")[0]; // Returns today's date
}

function createEvent(name, date = getDefaultDate()) {
  console.log(`Event: ${name} on ${date}`);
}

createEvent("Conference");
// Event: Conference on 2025-12-04

createEvent("Workshop", "2025-12-15");
// Event: Workshop on 2025-12-15

Important: Default parameter expressions are calculated at call time, not definition time:

javascript
let counter = 0;

function increment(value = ++counter) {
  console.log(value);
}

increment(); // 1 (counter becomes 1)
increment(); // 2 (counter becomes 2)
increment(10); // 10 (doesn't use default value, counter stays at 2)
increment(); // 3 (counter becomes 3)

2. Referencing Other Parameters

Default parameters can reference preceding parameters:

javascript
function createUser(
  firstName,
  lastName,
  fullName = `${firstName} ${lastName}`
) {
  return {
    firstName: firstName,
    lastName: lastName,
    fullName: fullName,
  };
}

console.log(createUser("John", "Doe"));
// { firstName: "John", lastName: "Doe", fullName: "John Doe" }

console.log(createUser("Jane", "Smith", "Dr. Jane Smith"));
// { firstName: "Jane", lastName: "Smith", fullName: "Dr. Jane Smith" }

However, you cannot reference subsequent parameters because they haven't been initialized yet:

javascript
// ❌ Error: cannot reference later parameters
// function invalid(a = b, b = 2) {} // ReferenceError: Cannot access 'b' before initialization

// ✅ Correct: can reference preceding parameters
function valid(a = 1, b = a * 2) {
  console.log(a, b);
}

valid(); // 1 2
valid(5); // 5 10

3. Complex Expressions

Default parameters can be any expression, including ternary operators, logical operations, etc.:

javascript
function calculatePrice(basePrice, discount = basePrice > 100 ? 0.1 : 0.05) {
  let finalPrice = basePrice * (1 - discount);
  console.log(
    `Original: $${basePrice}, Discount: ${
      discount * 100
    }%, Final: $${finalPrice}`
  );
}

calculatePrice(150);
// Original: $150, Discount: 10%, Final: $135

calculatePrice(80);
// Original: $80, Discount: 5%, Final: $76

4. Object and Array Literals

javascript
function createConfig(options = { debug: false, retries: 3 }) {
  console.log(`Debug mode: ${options.debug}`);
  console.log(`Max retries: ${options.retries}`);
}

createConfig();
// Debug mode: false
// Max retries: 3

createConfig({ debug: true, retries: 5 });
// Debug mode: true
// Max retries: 5

Combining with Destructuring

Default parameters are very powerful when combined with destructuring assignment:

Object Destructuring Parameters

javascript
function createUser({ name, age = 18, role = "user", active = true } = {}) {
  // Note the = {} at the end provides default value for the entire parameter
  return { name, age, role, active };
}

console.log(createUser({ name: "Alice" }));
// { name: "Alice", age: 18, role: "user", active: true }

console.log(createUser({ name: "Bob", age: 25, role: "admin" }));
// { name: "Bob", age: 25, role: "admin", active: true }

console.log(createUser());
// { name: undefined, age: 18, role: "user", active: true }

Note the role of = {} at the end of the function parameters - it provides a default value for the entire parameter object. Without it, if createUser() is called without parameters, it will try to destructure undefined, causing an error:

javascript
// ❌ No default empty object
function createUserBad({ name, age = 18 }) {
  return { name, age };
}

// createUserBad(); // TypeError: Cannot destructure property 'name' of 'undefined'

// ✅ Has default empty object
function createUserGood({ name, age = 18 } = {}) {
  return { name, age };
}

createUserGood(); // { name: undefined, age: 18 }

Array Destructuring Parameters

javascript
function processCoordinates([x = 0, y = 0, z = 0] = []) {
  console.log(`Position: (${x}, ${y}, ${z})`);
}

processCoordinates([10, 20, 30]);
// Position: (10, 20, 30)

processCoordinates([5, 10]);
// Position: (5, 10, 0)

processCoordinates();
// Position: (0, 0, 0)

Combining with Rest Parameters

Default parameters can be used with rest parameters, but rest parameters cannot have default values (because they are always an array):

javascript
function logMessages(level = "info", ...messages) {
  console.log(`[${level.toUpperCase()}]`);
  messages.forEach((msg) => console.log(`  ${msg}`));
}

logMessages(); // [INFO] (no messages)

logMessages("error", "Connection failed", "Retrying...");
// [ERROR]
//   Connection failed
//   Retrying...

logMessages(undefined, "Using default level");
// [INFO]
//   Using default level

Practical Application Scenarios

1. API Request Configuration

javascript
function fetchData(
  url,
  { method = "GET", headers = {}, timeout = 5000, retries = 3 } = {}
) {
  console.log(`Fetching ${url}`);
  console.log(`Method: ${method}`);
  console.log(`Timeout: ${timeout}ms`);
  console.log(`Max retries: ${retries}`);
  console.log(`Headers: ${JSON.stringify(headers)}`);
  // Actual fetch logic...
}

// Use default values
fetchData("/api/users");

// Override partial default values
fetchData("/api/posts", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
});

2. Data Formatting

javascript
function formatCurrency(
  amount,
  currency = "USD",
  locale = "en-US",
  options = {}
) {
  return new Intl.NumberFormat(locale, {
    style: "currency",
    currency: currency,
    ...options,
  }).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

3. Creating HTML Elements

javascript
function createElement(tag, content = "", attributes = {}, children = []) {
  let attrs = Object.entries(attributes)
    .map(([key, value]) => `${key}="${value}"`)
    .join(" ");

  let attrsString = attrs ? " " + attrs : "";
  let childrenHTML = children.join("");

  return `<${tag}${attrsString}>${content}${childrenHTML}</${tag}>`;
}

console.log(createElement("h1", "Welcome"));
// <h1>Welcome</h1>

console.log(createElement("div", "", { class: "container", id: "main" }));
// <div class="container" id="main"></div>

console.log(
  createElement("ul", "", {}, ["<li>Item 1</li>", "<li>Item 2</li>"])
);
// <ul><li>Item 1</li><li>Item 2</li></ul>

4. Pagination and Filtering

javascript
function getUsers({
  page = 1,
  limit = 10,
  sortBy = "createdAt",
  order = "desc",
  filters = {},
} = {}) {
  console.log(`Fetching page ${page} (${limit} items per page)`);
  console.log(`Sort by: ${sortBy} (${order})`);
  console.log(`Filters: ${JSON.stringify(filters)}`);
  // Actual database query...
}

getUsers(); // Use all default values
getUsers({ page: 2 }); // Only change page number
getUsers({ limit: 20, sortBy: "name", order: "asc" });
getUsers({ filters: { role: "admin", active: true } });

Common Pitfalls and Best Practices

1. Be Careful When Default Values Are Reference Types

If default values are objects or arrays, they will be reused on each call (unless recalculated):

javascript
// ⚠️ Potential issue: sharing the same object
let defaultOptions = { debug: false };

function configure(options = defaultOptions) {
  options.debug = true; // Modifies the default object
  console.log(options);
}

configure(); // { debug: true }
defaultOptions.debug; // true - default object was modified!

// ✅ Better: create new object each time
function configureSafe(options = { debug: false }) {
  options.debug = true;
  console.log(options);
}

// Or use destructuring to create new object
function configureBest(options = {}) {
  let config = { debug: false, ...options };
  config.debug = true;
  console.log(config);
}

2. Evaluation Order of Default Values

Default values are evaluated from left to right, preceding parameters can be referenced by following ones:

javascript
function buildQuery(table, where = {}, limit = 10, offset = limit * 0) {
  console.log(`SELECT * FROM ${table}`);
  if (Object.keys(where).length > 0) {
    console.log(`WHERE ${JSON.stringify(where)}`);
  }
  console.log(`LIMIT ${limit} OFFSET ${offset}`);
}

buildQuery("users");
// SELECT * FROM users
// LIMIT 10 OFFSET 0

buildQuery("posts", { published: true }, 20);
// SELECT * FROM posts
// WHERE {"published":true}
// LIMIT 20 OFFSET 0

3. Avoid Complex Default Value Expressions

Although default values can be complex expressions, overly complex ones reduce readability:

javascript
// ❌ Too complex
function process(
  data,
  transform = (data) =>
    data
      .filter((x) => x > 0)
      .map((x) => x * 2)
      .reduce((a, b) => a + b, 0)
) {
  return transform(data);
}

// ✅ Clearer: extract complex logic
function defaultTransform(data) {
  return data
    .filter((x) => x > 0)
    .map((x) => x * 2)
    .reduce((a, b) => a + b, 0);
}

function processBetter(data, transform = defaultTransform) {
  return transform(data);
}

4. null vs undefined

Remember that null does not trigger default values:

javascript
function greet(name = "Guest") {
  console.log(`Hello, ${name}!`);
}

greet(undefined); // Hello, Guest!
greet(null); // Hello, null!

// If you need to treat null as missing value, manually check
function greetBetter(name = "Guest") {
  name = name === null ? "Guest" : name;
  console.log(`Hello, ${name}!`);
}

greetBetter(null); // Hello, Guest!

5. Required Parameters First, Optional Parameters Later

Place required parameters first, optional parameters with default values last:

javascript
// ✅ Good: required parameters first
function createArticle(
  title,
  content,
  author = "Anonymous",
  published = false
) {
  return { title, content, author, published };
}

// ❌ Bad: required parameters last
function createArticleBad(
  author = "Anonymous",
  published = false,
  title,
  content
) {
  // Need to pass preceding parameters even if you want to use default values
  return { title, content, author, published };
}

createArticle("My Post", "Content here");
// { title: "My Post", content: "Content here", author: "Anonymous", published: false }

// createArticleBad is awkward to use
createArticleBad(undefined, undefined, "My Post", "Content here");

Summary

Default parameters are an important feature introduced by ES6, making function parameter handling more elegant and robust.

Key points:

  • Default parameters take effect when the parameter is undefined, not including other falsy values like null
  • Default values can be any valid JavaScript expression: literals, expressions, function calls, etc.
  • Default values are calculated at each call time, not definition time
  • Can reference preceding parameters, but not subsequent parameters
  • Very powerful when combined with destructuring, but remember to provide default empty object/array
  • Can be combined with rest parameters, but rest parameters themselves cannot have default values
  • Avoid mutable reference types as default values to prevent accidental modifications
  • Keep default value expressions simple, complex logic should be extracted outside the function
  • Place required parameters first, optional parameters later
  • Using object parameter patterns can handle multiple optional parameters more flexibly

Default parameters significantly improve code readability and robustness and are standard practice in modern JavaScript development. Combined with destructuring and rest parameters, you can create very flexible and elegant function interfaces.