Default Parameters in JavaScript in More Depth

Abstract image used to represent Default Parameters in JavaScript in More Depth
Image by Diane Picchiottino.

Following on from an article I wrote earlier this month where I covered some of the basics of default parameters in JavaScript, I wanted to delve a little deeper. So, now that we've covered the basics of default parameters, let's look at some more complex examples, and take a look at some best practices and potential pitfalls to keep in mind when using them.

First of all, it's important to remember that default parameters can be any valid expression, not just simple values. This means that you can use function calls, object literals, and other expressions as default parameter values. For example:

const getUser = (id, options = { includeDetails: true }) => {
  // function logic
};

getUser(123);
// options parameter will use default value { includeDetails: true }

getUser(123, { includeDetails: false });
// options parameter will use passed in value { includeDetails: false }

I accept this is a more abstract example, but what we're doing here is using an object literal as the default value for the options parameter. This lets us provide several defaults without cluttering the function signature. They can then be overridden when we call the function.

Be careful when a default expression has side effects. It is evaluated when the corresponding argument is omitted or explicitly undefined, rather than on every call regardless of the arguments.

const logDate = (date = new Date()) => {
  console.log(date);
};

logDate();
// outputs current date and time

setTimeout(() => logDate(), 1000);
// outputs a new date and time one second later

Here we're using new Date() as the default for the date parameter. Each call that omits that argument evaluates the expression afresh. In this example, the calls to logDate are a second apart, so they log different times.

We already get a fresh date on each call that uses the default. Passing a function gives us a different option: the caller can choose how the date is obtained, and logDate decides when to call that function. For example:

const logDate = (getDate = () => new Date()) => {
  console.log(getDate());
};

logDate();
// outputs current date and time

setTimeout(() => logDate(), 1000);
// outputs current date and time after one second

In this updated example, the default value of getDate is a function. The default function is created when the argument is omitted or undefined, and logDate then invokes it with getDate() to obtain the date for that call. Without the final parentheses it would log the function itself rather than a Date.

Another potential pitfall to keep in mind when using default parameters is that they can make it harder to distinguish between intentional and unintentional missing arguments. If a function has several parameters with default values, it may be difficult to tell which ones were intentionally left out and which ones were simply forgotten. In general, it's a good practice to use default parameters sparingly and only when they improve the readability and maintainability of your code.


The Wrap‑up

So, here are a few key takeaways:

Default parameters are a feature in JavaScript that allows us to provide default values for function parameters.

  • Default parameters are defined in the function signature and are used when a parameter is not passed in or is passed in with the value of undefined.
  • Default parameters can be any valid expression, not just simple values. This allows us to use function calls, object literals, and other expressions as default parameter values.
  • Default expressions run when the argument is omitted or undefined. Keep any side effects deliberate; a callback is useful when the caller needs to supply the behaviour instead of a value.
  • Default parameters can make it harder to distinguish between intentional and unintentional missing arguments, so use them sparingly and only when they improve the readability and maintainability of your code.

Planning a platform change?

I help teams make difficult platform work clearer, from architecture decisions and migrations to launch recovery, performance, and search visibility.