15

I have a function, in Javascript

function asd(foo, bar, baz) {
    // ...
}

And each of those 3 arguments is required.

Suppose I have those arguments grouped as an object:

var x = {
    foo: 10,
    bar: 20,
    baz: 30
};

Can I call function asd just by giving object x somehow "unpacked"? Can this be done generically? i.e., not knowing the signature of asd.

I'm searching for something like kwargs unpacking in Python:

def asd(foo, bar, baz):
    # ...

kwargs = {'foo': 10, 'bar': 20, 'baz': 30}
asd(**kwargs)

A "no, not possible" answer is acceptable. I just want to know whether this is possible in Javascript.

André Staltz
  • 13,304
  • 9
  • 48
  • 58
  • There's no built-in way to do that. You could write a function to do it. *edit* actually writing a function would be problematic, since JavaScript imposes no particular ordering on properties of an object. – Pointy Feb 23 '14 at 14:08
  • A function for that would be fine. But how? – André Staltz Feb 23 '14 at 14:09
  • 2
    Like this? http://stackoverflow.com/questions/5453636/how-can-i-unpack-an-object-into-a-functions-scope – James Feb 23 '14 at 14:10
  • 2
    Sorry, I was mistaken - you really can't predict the ordering of object properties when you get them via `Object.keys()` or a `for ... in` loop. Because that's undefined, you can't control the order in which you pass the property values to the function. – Pointy Feb 23 '14 at 14:10
  • @James that question is somewhat different I think. This question is about how to use object properties as arguments in a function call. – Pointy Feb 23 '14 at 14:12

4 Answers4

13

This is now possible in JavaScript. The other comment stating that it is not possible natively is not correct (and wasn't at time of posting). It was added roughly a year after you asked - adding an answer for people landing here many years later.

Your function simply needs to change to:

function asd({foo, bar, baz}) {
    // ...
}

and you call it like this:

var x = {
    foo: 10,
    bar: 20,
    baz: 30
};
asd(x);

See MDN Destructuring Assignment.

Aaron J
  • 153
  • 1
  • 7
  • 4
    This does not maintain the function signature `function add(foo, bar, baz)` as the original post wanted. There is still no built-in way to achieve this in JavaScript. – juliomalves Jan 08 '21 at 22:11
  • 4
    Hmm, you're right, I missed the "i.e., not knowing the signature of asd" portion. My mistake. Nonetheless, some google searches for object unpacking lead to this post, so I'll leave my answer up as an alternative for people getting here indirectly. – Aaron J Jan 13 '21 at 15:39
3
function asd(foo, bar, baz) {
    console.log(foo, bar, baz);
}

var x = {
    foo: 10,
    bar: 20,
    baz: 30
};

var args = [];
for(var i in x){
    args.push(x[i]);
}
asd.apply(window, args);

But, like other people said, ordering is a problem. With for in you get the properties in the order that they were defined. If that's what you want, then this solution can work for you. But mapping parameters by name is not possible.

spassvogel
  • 3,479
  • 2
  • 18
  • 23
3

Just making things clearer for people coming from Google like me:

No, this is not possible natively in JavaScript.

You have to implement something custom to achieve this (see the other answer).

Matthieu Napoli
  • 48,448
  • 45
  • 173
  • 261
1

Answring to this old post, I implemented this solution that relies on the spread operator and on parsing the function argument names. It can be improved to accept also arrow function.

Function.prototype.kwargs = function(argsObj){
  const rawArgs = this.toLocaleString().match(/\(.*\)\{/);
  if(!rawArgs){
    console.error("args parsing failed");
    return;
  }
  const argsList = rawArgs[0].replace(/\(|\)|\{/g, '').split(',');
  const args = argsList.map((param) => argsObj[param.trim()] || param);
  return this(...args);
}

function sum(a,b){
  return a+b;
}
const input = {a:1, b:2}
console.log(sum.kwargs({a:1, b:2}))


function sum2(a12,   b){
  return a12+b;
}
const input2 = {a12:1, b:2};
console.log(sum2.kwargs(input2));
Greedo
  • 3,438
  • 1
  • 13
  • 28