ES6 destructuring function parameter - naming root object
Asked Answered
M

5

29

Is there a way to retain the name of a destructured function argument? I.e., the name of the root object?

In ES5, I might do this (using inheritance as a metaphor to make the point):

// ES5:
var setupParentClass5 = function(options) {
    textEditor.setup(options.rows, options.columns);
};

var setupChildClass5 = function(options) {
    rangeSlider.setup(options.minVal, options.maxVal);
    setupParentClass5(options); // <= we pass the options object UP
};

I'm using the same options object to hold multiple configuration parameters. Some parameters are used by the parent class, and some are used by the subclass.

Is there a way to do this with destructured function arguments in ES6?

// ES6:
var setupParentClass6 = ({rows, columns}) => {
    textEditor.setup(rows, columns);
};

var setupChildClass6 = ({minVal, maxVal}) => {
    rangeSlider.setup(minVal, maxVal);
    setupParentClass6( /* ??? */ );  // how to pass the root options object?
};

Or do I need to extract all of the options in setupChildClass6() so that they can be individually passed into setupParentClass6()?

// ugh.
var setupChildClass6b = ({minVal, maxVal, rows, columns}) => {
    rangeSlider.setup(minVal, maxVal);
    setupParentClass6({rows, columns});
};
Mandymandych answered 14/3, 2015 at 16:2 Comment(2)
arguments[0] would do, but no, you cannot name them at the same time as destructuring.Scrivener
For naming intermediate objects, see Destructuring nested objects: How to get parent and it's children values?Scrivener
F
23

I have the 'options' arguments on too many places myself. I would opt for 1 extra line of code. Not worthy in this example, but a good solution when having destructuring on more lines.

const setupChildClass6 = options => {
    const {minVal, maxVal} = options;
    rangeSlider.setup(minVal, maxVal);
    setupParentClass6(options); 
};
Flawy answered 7/12, 2015 at 21:26 Comment(1)
Agreed -- this is what I've been using as well. We've all learned to use es6 much better in the last few months!Mandymandych
C
7

You cannot do it directly in the arguments, but you can extract the values afterward:

function myFun(allVals) {
    const { val1, val2, val3 } = allVals;
    console.log("easy access to individual entries:", val2);
    console.log("easy access to all entries:", allVals);
}
Converter answered 17/10, 2015 at 10:49 Comment(5)
And even better, you can use const here :-)Scrivener
Oh but that may depends on what he wants to do with them, don't you think?Converter
Well there are many people who argue that const should be the default. Of course if you wish to reassign, you'd need let.Scrivener
tl;dr poor intellisense. The problem with this approach is that you won't have any indication of allVals structure when using myFunPerrotta
That’s true. If this is a concern to you you can add JSDoc. Or you could do the opposite (destructure the args then restructure them underneath), but it is a little bit more verbose and you will have to repeat each vars twice.Converter
C
6

You cannot use destructuring and simple named positional argument for the same parameter at the same time. What you can do:

  1. Use destructuring for setupParentClass6 function, but old ES6 approach for setupChildClass6 (I think this is the best choice, just make name shorter):

    var setupChildClass6 = (o) => {
      rangeSlider.setup(o.minVal, o.maxVal);
      setupParentClass6(o); 
    };
    
  2. Use old arguments object. But arguments can slow down a function (V8 particular), so I think it's a bad approach:

    var setupChildClass6 = ({minVal, maxVal}) => {
      rangeSlider.setup(minVal, maxVal);
      setupParentClass6(arguments[0]); 
    };
    
  3. ES7 has proposal for rest/spread properties (if you don't need minVal and maxVal in setupParentCalss6 function):

    var setupChildClass6b = ({minVal, maxVal, ...rest}) => {
      rangeSlider.setup(minVal, maxVal);
      setupParentClass6(rest);
    };
    

    Unfortunately it's not ES6.

Candleberry answered 14/3, 2015 at 16:37 Comment(5)
Thanks, super-helpful. Actually #2 is closest to the intention, and the slowdown will not be relevant given how the function is used. For the moment, I'm using Babel, so perhaps it will get rest properties on objects soon.Mandymandych
The usage of arguments as shown in #2 actually is not slow. But I'd consider it unreadable.Scrivener
Rest properties might not solve your problem, as it does take minVal and maxVal out of the options objects. Beware.Scrivener
can you elaborate on why and how much using arguments would slow down the function?Perrotta
If I use approach 2 in an arrow function, I get "arguments is not defined" I don't think you can use arguments in an arrow function, can you?Baht
P
4

Personally I'd rather destructure within the function body, but for completeness I wanted to show that it's technically possible to access both the "root" object and destructured properties via parameters.

To do we take advantage of the fact that parameters have access to preceding parameters in their default values. That allows you to define a second parameter that destructures the first one:

var setupChildClass6 = (options, {minVal, maxVal}=options) => {
    rangeSlider.setup(minVal, maxVal);
    setupParentClass6(options);
};

So this function takes two arguments and defaults to using the first value of the first argument for the second argument.

Of course you'd have to take great care that it's communicated properly what the purpose of this is.

Promptbook answered 7/4, 2022 at 7:57 Comment(1)
This is so weird, but also the best - and as far as I can tell only - option which respects "lambda" style functions, where there is no => { statements... } body (just one return expression). In another world where you want to return the results of both expressions: setupChildClass6 = (options, {minVal, maxVal} = options) => ({childSetup: rangeSlider.setup(minVal, maxVal), parentSetup: setupParentClass6(options)})Canine
F
0

This is the best I could come up with. Not great, but far better (IMO) than doing the destructing on another line. It gives you a heads-up in a way too that you are going to provide options first and foremost, and also that you are destructuring it in the function...

var setupParentClass6 = options => (({rows, columns}) => {
    textEditor.setup(rows, columns);
})(options);

var setupChildClass6 = options => (({minVal, maxVal}) => {
    rangeSlider.setup(minVal, maxVal);
    setupParentClass6(options);
})(options);
Forwardlooking answered 26/6, 2020 at 15:20 Comment(1)
Thanks! I wrote my original question back in the early days of ES6, before we had rest & spread. Now that we do, I write my code in the style of the answer given by Alex above (example 3)Mandymandych

© 2022 - 2024 — McMap. All rights reserved.