8

I am currently using the object destructuring pattern with default parameters described in that answer to ES6 Object Destructuring Default Parameters.

(function test({a = "foo", b = "bar"} = {}) {
  console.log(a + " " + b);
})();

I would like to be able to access the object parameter without assigning it to a variable in the function body and explicitly listing each key.

(function test({a = "foo", b = "bar"} = {}) {
  const options = {a, b};
  console.log(options);
})();

I tried naming the object argument, but the function looses the ability to resolve missing keys to their default value.

(function test(options = {a = "foo", b = "bar"} = {}) {
  console.log(options);
})();

It seems to be ignoring the default parameters when destructuring into a named argument.

Is this part of the ES6 spec? Is there a way to achieve the desired behavior without additional code in the function body?


Edit: I removed a superfluous example that did not add context to the question.

Community
  • 1
  • 1
Jared Deckard
  • 633
  • 6
  • 15
  • Default params are for destructured variables. If you're want to keep them as object properties, you need to do this in body - as the answer explains. – Estus Flask Mar 29 '17 at 14:39
  • @estus Do you have a source for that information? This is a technical question about the ES6 spec, not general programming practices. – Jared Deckard Mar 29 '17 at 15:08
  • No source, this is the conclusion made from what ES6 destructuring is capable of. The thing you're trying to do cannot be handled by function signature alone and should be done in function body. Btw, explicit restructuring in second snippet is not a bad thing by itself. It allows to exclude invalid keys from the object. – Estus Flask Mar 29 '17 at 15:35
  • Your third snippet works like `function test(options = ({a = "foo", b = "bar"} = {}))` - that's not what you want – Bergi Mar 29 '17 at 17:28
  • @Bergi That makes sense. If you post that as an answer I will select it. – Jared Deckard Mar 29 '17 at 20:54
  • It's also worth noting that this code errors in Firefox. It seems like my confusion may just been caused by V8's implementation allowing undefined behavior. – Jared Deckard Mar 29 '17 at 21:17

1 Answers1

6

Honestly, I think you're overcomplicating this. Default parameters are not compulsory -- in this case your code can be cleaner without it.

I would simply take the object options as the parameter and do the destructuring within the body of the function, after assigning default values.

function test(options) {
    options = Object.assign({a: 'foo', b: 'bar'}, options);
    let {a, b} = options;
    console.log(options, a, b);
}

test(); // foo bar
test({a: 'baz'}); // baz bar
test({b: 'fuz'}); // foo fuz
test({c: 'fiz'}); // foo bar

With particular regard to your final snippet:

(function test(options = {a: "foo", b: "bar"}) {
  console.log(options);
})({a: "baz"});

The problem is that a default parameter is used when the value passed is undefined. Here, the value passed is {a: "baz"}. That is not undefined, so the default parameter is ignored. Objects are not merged automatically.

More broadly in answer to your question: there is no way of getting both an object and destructuring some of its properties in the parameters of a method. Frankly, I'm grateful, because function signatures can be hard enough to read at first glance as it is.

lonesomeday
  • 233,373
  • 50
  • 316
  • 318
  • 1
    I appreciate your practical answer, but I am mostly interested in why the syntax I mentioned is valid, yet produces unexpected results. – Jared Deckard Mar 29 '17 at 14:31
  • @JaredDeckard In that case perhaps you could make your question a little more specific. I'm not sure which results you find unexpected. – lonesomeday Mar 29 '17 at 14:34
  • "I tried naming the object argument, but the function looses the ability to resolve missing keys to their default value. ... Is this part of the ES6 spec? Is there a way to achieve the desired behavior without additional code in the function body?" I am not asking about the most practical way to achieve this functionality. I am asking about the ES6 spec as the closing question shows. – Jared Deckard Mar 29 '17 at 15:06
  • 1
    **Edit:** I removed the superfluous `options = {a: "foo", b: "bar"}` example since it did not add context to the question. `options = {a = "foo", b = "bar"} = {}` is valid, yet has unexpected results. I want to know if this is expected ES6 behavior. – Jared Deckard Mar 29 '17 at 16:25
  • Might as well use ES6 for `Object.assign`: `options = { a: 'foo', b: 'bar', ...options };` – Mingwei Samuel Apr 27 '20 at 20:33