DEV Community

loading...

Understanding Default Parameters in Javascript

kepta profile image Kushan Joshi ・4 min read

Javascript landed with native default parameters support with ES2015. Contrary to what one might think, it isn’t just a syntactic replacement for||(the logical OR operator).

Javascript before default parameters

Before the native support for default parameters, one would write something like this:

function filterEvil(array, evil) {
  evil = evil || 'darth vader';
  return array.filter(item => item !== evil);
}

To understand the code snipped above, one needs to keep the following things in mind:

  • Truthy/Falsy: Values in javascript can be categorized into truthy or falsy. In simple words, Javascript tries to convert a variable into a boolean value, to figure out the conditionals (eg. if, for loop). * Short Circuit Evaluation: The expression involving || is processed left to right. While processing left to right, the compiler is smart enough to not process the remaining items if it encounters a truthy value aka Short Circuiting.

In short, this is just a syntactic sugar for:

function filterEvil(array, evil) {
  if (!evil) {
    evil = 'darth vader';
  }
  return array.filter(item => item !== evil);   
}

The reason this method is so prevalent is that you can chain multiple ||’s and let short-circuiting take care of it. Imagine writing the same thing with if statements.

Now if your evil variable happens to be an empty string '' or any falsy value, the function above will assume it to be darth vader.

This can be okay in most cases, but it can cause those dreaded silent bugs in a more involving application. A great solution to this problem is within the language and it is called default parameters.

Javascript after default parameters

Let us use the same filterEvil example and see how it would look with default parameters.

function filterEvil(array, evil = 'darth vader') {
  return array.filter(item => item !== evil);   
}

At first glance, it looks neat and concise. But don’t let the looks deceive you! There is a lot going under the hood. It would be frivolous to assume that the default value would step in whenever evil is not supplied.

Let us look at some important details,

1. Distinction between null and undefined.

Javascript has two answers to the not present problem, null and undefined. ( null being a topic of controversy). The general consensus is that null is an explicit value to tell that there is no value. In the example below, we try to pass null as an argument.

const list = [ 'luke', 'leia', 'darth vader' ];
filterEvil(list, null); // [ 'luke', 'leia', 'darth vader' ]

The filterEvil function will not substitute evil with a default value when supplied with null or any falsy value.

The filterEvil function will substitute if and only if evil is undefined. Now you are in luck, as Javascript will automatically use undefined if you do not explicitly pass an argument:

const list = [ 'luke', 'leia', 'darth vader' ];
filterEvil(list); // [ 'luke', 'leia' ]

This is something to keep in mind when working on projects which rely heavily on null.* (Although, I worry a lot about the developers who use null/undefined interchangeably)*

2. Evaluated Left to Right

The default parameters are evaluated from left to right. This is indeed confusing but a very powerful feature. Let us look at an example.


function findEvil(array, evil = 'darth vader', respect = 'Bad ' + evil) {
  if (array.find(item => item === evil)) {
     return respect;
  }
}

findEvil(list); // Bad darth vader;

findEvil(list, 'luke'); // Bad luke;

As you can see, we can reuse a param on the left as a default param for something on the right. Note that respect will get the evil param with the default check applied.

3. Calling a function

You can also call a function and use the value returned as a default parameter. In short this lets you call a regular function and compute the default parameter on the fly.

function whoIsEvilNow() {
  if (time > 2014) {
    return 'J. J. Abrams'
  }
  return 'darth vader';
}


function findEvil(array, evil = whoIsEvilNow()) {
  return array.find(item => item === evil);
}

4. Evaluated at call time

Now this feature is what confuses me the most. Let us look at an example. Confusing but a very powerful feature. Let us look at an example.

function filterEvil(array = [], evil = 'darth vader') {
  return array.filter(item => item === evil)
}

Each time you call an array without an argument, a new instance of an empty array is created.

Each time you call filterEvil without an argument, a new instance of an empty array is created. Now, this might become a problem if you are into into selectors and memoization. This behaviour can easily fool your dependant logic thinking something changed (I am talking to you React). For example, if you use reselect in your react project, your component can update unnecessarily as react will re-render with each new instance of the empty array.

Here are some cool tricks:

  • Required Param Check: You can use default params to enforce a required parameter. In the example below, we are enforcing evil as a required prop.
const isRequired = () => { throw new Error('param is required'); };

function filterEvil(array, evil = isRequired()) {
  return array.filter(item => item !== evil);   
}
  • Destructured Default Params: You can also use default params in a destructed expression.
function firstItem([first, second] = ['luke', 'skywalker']) {
  return first;
}

function findName({ name } = { name : 'darth' }) {
  return name;
}


I hope this post helped you in understanding default parameters.

Reach out to me on Twitter @kushan2020.

Discussion (1)

pic
Editor guide
Collapse
thibmaek profile image
Thibault Maekelbergh

Wonderful summary. Lots of things I didn't know about, especially that required param trick.