13

Is there a way to destructure a JS object in-place, instead of assigning the destructured variables to a scope?

Instead of doing this:

const { a, b, c } = obj;
someFunction(a, b, c);

I'd like to do this:

someFunction({a, b, c} from obj);

Or something functionally equivalent.

I'd like to do this in situations with these two stipulations:

  • I don't want to put the variable names into the enclosing scope.

  • I don't want to pass the whole object obj, therefore making the spread operator not an option.

The only option I'm left with is to use

someFunction(obj.a, obj.b, obj.c);

Which is fine in this case, but can lower readability when obj is instead a long identifier.

Is something like this possible? I tried using assignment in an expression as a workaround, but my IDE complained that it could not find names a, b, and c:

someFunction({a, b, c} = obj);
1
  • 2
    if you are targeting es6 environments you can define your function with a destructure parameter, eg function f({a,b,c}){ /*...*/ } or would you not want that since you dont want to pass the whole object Commented Apr 21, 2019 at 3:22

4 Answers 4

10

One option is to use .map to extract the value of each property you want, and spread it into the argument list:

someFunction(
  ...['a', 'b', 'c'].map(prop => obj[prop])
);

Destructuring requires the creation of intermediate variables, unfortunately, which you don't want.

Sign up to request clarification or add additional context in comments.

3 Comments

It is unfortunate that the intermediate is required, as it's just a needless performance penalty in some situations (like mine). As for your proposition, I like that it scales better than obj.property, but it's still quite verbose :/ enough so that I'm sure I couldn't throw it into production code without it failing code review.
Performance is rarely something to worry about - code readability matters more in 99% of situations, and if you want to keep the code DRY and want to be able to use the pattern for any number of properties, I'm pretty sure this is the best pattern you'll be able to find. If this fails code review because it's too verbose, I think that's an indicator that there's an issue with code review, rather than an issue with the code here
I agree with all of your statements, especially the last one (but sadly my opinion does not outweigh tradition). Maybe I should give it a go and see what happens.
6

An IIFE should work:

((({ a, b, c }) => someFunction(a, b, c))(obj);

1 Comment

I like that this works, but I dislike the performance penalty of making a function whenever I need to call someFunction. Also, akin to the other answer, this is unusual-enough looking that I don't think it'd pass code review :/.
2

This how I'd do it:

function foo( { x, y } ) {
    console.log( x, y );
}

foo( { y: 1, x: 2 } );     // 2 1


As for the OP's specific request to not pass the whole object (or declare variables in the global scope), destructuring the object to block-scoped variables would be the best way, IMHO.

const obj = { x: 1, y: 2 }

function foo( x, y ) {
    console.log( x, y );
}

{   let { x, y } = obj;
    foo( x, y );   // 1 2
}

console.log(x) // "ReferenceError: x is not defined

4 Comments

Can you explain how this resolves my issue? I don't see a strong relation.
You pass an object as an argument and destruct it as function parameters. I know that you've said that you don't want to pass the whole object as argument, still this's my recommendation.
Right, but this means I'd have to be the one writing the function. Great for my own code, but there are many situations where I don't have control over the function's parameters e.g. libraries, or most cost when coding for a company.
@@AnthonyMonterrosa if it's such a big issue, I would use this style as a wrapper function to your library call, eg function wrapLib({x, y}) { return library.expensiveCall(x,y)}. If the situation calls for it, the result can be cached. Alternatively, the TC39 are always looking for unique improvements to JS.
-2

Function call from object destructuring with unknown parameters

This is needed when you can't access or change the function declaration, or simply you don't want to.

const getParamNames = func => {
    const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
    const ARGUMENT_NAMES = /([^\s,]+)/g;
    let fnStr = func.toString().replace(STRIP_COMMENTS, '');
    let result = fnStr.slice(fnStr.indexOf('(') + 1,
    fnStr.indexOf(')')).match(ARGUMENT_NAMES);
    if (result === null)
        result = [];
    return result;
}

const callFunctionFromObject = (func, obj) => {
    let params = getParamNames2(func)
    return func(...params.map(prop => obj[prop]))
}

Example usage:

// Function declared somewhere:
var logForStackOverflow = (a, b, c) => console.log(a, b, c)

// Example  1
callFunctionFromObject(logForStackOverflow, {a: 1});
1 undefined undefined
// Example 2
callFunctionFromObject(logForStackOverflow, {b: 1})
undefined 1 undefined
// Example 3
callFunctionFromObject(logForStackOverflow, {b: "hello", c:3, a:[1, 2, 3]})
[1, 2, 3] "hello" 3

Thanks to CertainPerformance's solution.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.