0

Say I have this function:

function doSomething(uno, dos, tres) {
    // do something
}

and this object:

{
    dos: 'foo',
    tres: 'bar',
    uno: 'baz'
}

How do I call doSomething given this mapping? I tried using apply, but it seems that apply must take a list instead of an object.

John Hoffman
  • 17,857
  • 20
  • 58
  • 81
  • JavaScript doesn't support named arguments, so you'll have to convert them into an array of positional arguments. Related: [Named parameters in javascript](http://stackoverflow.com/questions/11796093/named-parameters-in-javascript) – Blender Jun 05 '13 at 00:33
  • What if the only relationship between the object and the function parameters is captured in the variable names? – John Hoffman Jun 05 '13 at 00:35
  • You'd do `doSomething.apply(this, names.map(function(name) {return obj[name];}))` –  Jun 05 '13 at 00:42

5 Answers5

3

You can refer each property in the object as given above using . as given below.

doSomething(obj.uno, obj.dos, obj.tres)
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
1

Function.apply can only be used to apply an array-like object1 to a function call. There is no equivalent to Pythons "keyword argument expansion" and this must be done manually:

var opts = {
    dos: 'foo',
    tres: 'bar',
    uno: 'baz'
}
doSomething(opts.uno, opts.dos, opts.tres)

If we started with an array-like object:

var arr = ['baz', 'foo', 'bar']
doSomething.apply(window, arr)

Or putting the two together (so that the composition of to a parameter sequence can be handled earlier):

var arr = [opts.uno, opts.dos, opts.tres]
doSomething.apply(window, arr)

While it may be possible (with non-obfuscated code) to use Function.toString2 to extract the parameter names, do some parsing, and then write some code to "apply" a general object (or construct an array that is then applied), this is not supported directly by the ECMAScript specification.


1 An array-like object is one that has a length and properties named 0..length-1. (Old versions of FF had a bug, but that has been fixed for a long time.)

2 The wording of the specification ("An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration..") makes me believe that a conforming ES5 implementation ought to produce output usable for this purpose - however, this will likely vary by implementation and I have not explored such usage in practice.

user2246674
  • 7,621
  • 25
  • 28
1

You can try using:

function doSomething() {
   for (var i = 0, l = arguments.length; i<l; i++) {
      //dosomething
      console.log(arguments[i]);
   }
}

obj=['foo', 'bar', 'baz'];
doSomething.apply(null, obj);

Apply accepts array as second parameter but if you insist on using object you can easily convert this object to Array and then use it with apply.

function objectToArray(obj) {
   var arr = [];
   for (field in obj) 
      arr.push(field);
   return arr;
}
Boris D. Teoharov
  • 2,319
  • 4
  • 30
  • 49
0

You can do something like this:

Function.prototype.applyNamed = function (g, argArr) {
    var self = this;    
    var argNames = self.toString().match(/function\s+\w*\s*\((.*?)\)/)[1].split(/\s*,\s*/);
    var applyArgs = [];
    for(var i=0; i<argNames.length; i++){        
        applyArgs.push(argArr[argNames[i]]);        
    }
    self.apply(self, applyArgs);
}

function doSomething(uno, dos, tres) {    
    console.log('uno = ' + uno);
    console.log('dos = ' + dos);
    console.log('tres = ' + tres);
}

var namedArgs = {
    dos: 'foo',
    tres: 'bar',
    uno: 'baz'
};

doSomething.applyNamed(this, namedArgs);

Should print:

uno = baz  
dos = foo  
tres = bar

See the demo; credit.

Community
  • 1
  • 1
muratgu
  • 7,241
  • 3
  • 24
  • 26
0

Well, if you really want to, you actually can call a method using named parameters, but it requires parsing the string representation of the function. Whether or not you should use this technique is debatable, but here is how you would do it:

function doSomething(uno, dos, tres) {
    // do something
}

var arguments = {
    dos: 'foo',
    tres: 'bar',
    uno: 'baz'
}

function callFunction(fn, namedArguments) {
    var orderedArguments = [];
    var parameterRegExp = /\(([\s\w\$,]*)\)/;

    parameterRegExp.exec(fn.toString())[1].replace(/\s/g, '').split(',').forEach( function(parameterName) {
        orderedArguments.push(namedArguments[parameterName]);
    });

    return fn.apply(this, orderedArguments);
}

callFunction(doSomething, arguments);
jSource
  • 524
  • 2
  • 5