Anthony
Anthony

Reputation: 1133

Object destructuring for function calls

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:

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);

Upvotes: 13

Views: 9399

Answers (4)

Alberto
Alberto

Reputation: 1489

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.

Upvotes: -2

Khalid Ali
Khalid Ali

Reputation: 1224

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

Upvotes: 2

Jack Bashford
Jack Bashford

Reputation: 44107

An IIFE should work:

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

Upvotes: 6

CertainPerformance
CertainPerformance

Reputation: 370759

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.

Upvotes: 10

Related Questions