Search code examples
javascriptecmascript-6destructuringdouble-splat

Parameter destructuring (equivalent of python's double-splat)


In python I can pass a dict whose keys match parameters' names with the ** (double-splat) operator:

def foo(a, b):
    print (a - b)

args = {'b': 7, 'a': 10}

foo(**args) # prints 3

How to do the same in ES6? This doesn't work:

function foo(a, b) {
    console.log(a - b)
}

args = {b: 7, a: 10}

foo(...args)

NB: I'm looking for a solution that wouldn't involve changing the signature of foo because I want it to be used either way (with and without destructuring). So the following should work:

foo(<magic>args);

foo(123, 456);

Bonus question: why is the error message "undefined is not a function"? What exactly is undefined here?

(As answered by @Nina Scholz in the comments, this is because ... requires its argument to have Symbol.iterator, which is not defined for objects).


Solution

  • How to do the same in ES6?

    There are no named arguments in JS, only positional ones. So the answer is: you can not.

    What you can do is either emulate named arguments via object passing, as @Andy suggested.

    function foo({ a, b }) {
        console.log(a - b);
    }
    
    let args = { b: 7, a: 10 };
    
    foo(args);
    

    Or you could make args to be an array, so you can destruct it into positional arguments.

    function foo(a, b) {
        console.log(a - b);
    }
    
    let args = [10, 7];
    
    foo(...args);
    

    Okay-okay, just for the sake of the argument: it is possible to write a function that will extract parameters of foo and yield properties of args in required order.

    function * yolo(args, fn) {
        const names = fn.toString().match(/\(.+\)/)[0]
                        .slice(1, -1).split(',')
                        .map(x => x.trim());
    
        while (names.length) {
            yield args[names.shift()];
        }
    }
    
    function foo(a, b) {
        console.log(a - b);
    }
    
    const args = { b: 7, a: 10 };
    
    foo(...yolo(args, foo));
    

    I would not dare to use it in production though.