On a couple of occasions I’ve found that students in one of my workshops were confused by the syntax, so here’s a detailed post about the possible combinations of object parameters and default values in JavaScript.

If you’d like to play with the samples yourself, check out this GitHub repo.

Accept a default value for a parameter — basic stuff

To get things started, here’s a function that accepts a parameter, but also defines a default value for that parameter. So if the caller doesn’t pass the parameter, the default value is used.

function takeDefault(x = 42) {
  console.log(`Got x=${x}`);
}

// This gives us x = 42
takeDefault();

Note that there are details you may want to know about this basic feature, if it’s news to you — for instance, what happens if the parameter list is longer than here, and there are other parameters with or without default values? I recommend checking out existing documentation like this MDN page.

Now what if the parameter is an object?

If your function accepts an object as a parameter and you use destructuring, the individual object fields can also have defaults. These are applied selectively: if a given field is not part of the object passed by the caller, the default value is used instead, while the fields that are in fact passed by the caller are left untouched.

function takeObject({ x = 42, y }) {
  console.log(`Got x=${x} and y=${y}`);
}

// x = 42 in this case, y of course = 17
takeObject({ y: 17 });

// x = 101 now, y still 17
takeObject({ x: 101, y: 17 });

// No value given, x=42 and y=undefined
takeObject({});

One thing that should be obvious though is that the object itself is not an optional parameter in this case. So if you call the function takeObject without any parameter, you’ll receive an error:

takeObject();

'Uncaught TypeError TypeError: Cannot read properties of undefined (reading 'x')'

Let’s use a default object then

If you want to allow a caller to not pass any arguments at all, here’s the syntax for that:

function takeObjectWithDefault({ x = 42, y } = {}) {
  console.log(`Got x=${x} and y=${y}`);
}

// Now we don't have to pass an object if we don't want to
takeObjectWithDefault();

The potentially confusing part about this is that it appears as if the default object has no values at all now. But that’s not true!

The results are the same as if the caller had passed {}, which you have already seen in the previous section of this post.

In other words, if no value is passed by the caller, the empty object {} is substituted, and then the processing of the per-field defaults commences as before.

Note that theoretically the default object can override default values on the object level. This is probably not a good idea unless you have a very specific reason why it would be expected by the caller.

function takeObjectWithWeirdDefault({ x = 42, y } = { x: 101 }) {
  console.log(`Got x=${x} and y=${y}`);
}

takeObjectWithWeirdDefault();

Works the same for arrays

I mentioned the MDN page about default parameters above, and if you read it carefully you may have spotted an example there which looked similar to mine. For some strange reason though, the page focuses mainly on an array destructuring example:

function preFilledArray([x = 1, y = 2] = []) {
  return x + y;
}

True, this works exactly the same as the object syntax above! Interesting, yes. I have certainly used array destructuring before, however… I have no clue why MDN focuses on this as their primary example. When I think of a scenario where a function should receive a set of named values, my first thought is not to make this an array. Why would I? Well, in case this approach makes more sense to you than it does to me, rest assured that the same mechanism exist to support you.