Skip Navigation

Using default function parameters wrong and right in JavaScript

This is a short story of two JavaScript functions.

What they do is not important. I want to illustrate something about default function parameters.

First, I had a function getColorOnBackground, which took the parameters backgroundColor and, optionally, dark and light. It returned either dark or light:

function getColorOnBackground (
  backgroundColor,
  dark = 'text-gray-900',
  light = 'text-white'
  // `dark` and `light` are CSS classes
  ) {
  // Do some logic
  // Return either the `dark` or `light` values
}

It needed to set default parameters for dark and light, otherwise, they couldn't be optional.

The other function is getTitleColorFromData, and it calls the first one, getColorOnBackground.

function getTitleColorFromData (data) {
  if (data.backgroundColor) {
    // here call the other function
    return getColorOnBackground(data.backgroundColor)
  }
  // do other things
}

This function has at least one limitation, the option to use custom values for light and dark is lost. So, naively, what we want is:

function getTitleColorFromData (data, dark, light) {
  if (data.backgroundColor) {
    // here call the other function
    return getColorOnBackground(data.backgroundColor, dark, light)
  }
  // do other things
}

This will work.

However, as the function does more things, I expect that the number of arguments might increase. When this happens, I like to turn the optional arguments into an object.

This was my (dumb) try:

function getTitleColorFromData (data, { dark = null, light = null }) {
  if (data.backgroundColor) {
    // here call the other function
    return getColorOnBackground(data.backgroundColor, dark, light)
  }
  // do other things
}

I replaced dark, light by { dark = null, light = null }.

Here I was thinking: "if I don't pass light or dark values, just use the fallback set in the getColorOnBackground function". Setting the default values to null was a naive attempt to make them be falsy and thus fallback to the default parameters.

That was very wrong.

Default parameters - JavaScript | MDN says:

If default parameters exist, e.g. function myFunction (value = 'default') {...} they will only be used in two cases:

  • If no value is passed when we call the function

  • If a value is passed and is explicitly set to undefined

null is not the same as undefined, so default parameters are NOT used if the passed value is null!

So, in my (dumb) attempt above, the nested function getColorOnBackground would always return null. :(

How to fix it?

An obvious solution would be to set the default parameters to undefined explicitly:

function getTitleColorFromData (data, { dark = undefined, light = undefined }) {
  if (data.backgroundColor) {
    // here call the other function
    return getColorOnBackground(data.backgroundColor, dark, light)
  }
  // do other things
}

That's not a good solution though. It carries one problem: I'm forced to always pass a second argument, like so: getTitleColorFromData(myData, {}).

Otherwise I get an error:

Cannot destructure property 'dark' of 'undefined' as it is undefined.

Plus, it's also verbose.

Fortunately, there's a good solution to this small conundrum:

function getTitleColorFromData (data, { dark, light } = {}) {
  if (data.backgroundColor) {
    // here call the other function
    return getColorOnBackground(data.backgroundColor, dark, light)
  }
  // do other things
}

The { dark, light } = {} means: "Use an empty object as a default parameter if no argument is provided". With this, light and dark will automatically be undefined.

Not only that works, but is also more flexible:

  • The second argument is totally optional. This will work: getTitleColorFromData(myData)

  • We can set the value of both parameters. This will also work: getTitleColorFromData(myData, { dark: 'text-gray-900', light: 'text-gray-100' })

  • We can set a value for one of the parameters: getTitleColorFromData(myData, { light: 'text-gray-100' })

Isn't the power of default function parameters wonderful?