tags:

views:

8903

answers:

5

Hi all,

I'm looking for a trick about this. I know how to call a dynamic, arbitrary function in Javascript, passing specific parameters, something like this:

function mainfunc (func, par1, par2){
    window[func](par1, par2);
}

function calledfunc(par1, par2){
    // Do stuff here
}

mainfunc('calledfunc','hello','bye');

I know how to pass optional, unlimited parameters using arguments[] collection inside mainfunc, but, I can't figure how to send an arbitrary number of parameters to mainfunc to be sent to calledfunc dynamically; how can I accomplish something like this, but with any number of optional arguments (not using that ugly if-else)? :

function mainfunc (func){
    if(arguments.length == 3)
        window[func](arguments[1], arguments[2]);
    elseif(arguments.length == 4)
        window[func](arguments[1], arguments[2], arguments[3]);
    elseif(arguments.length == 5)
        window[func](arguments[1], arguments[2], arguments[3], arguments[4]);
}

function calledfunc1(par1, par2){
    // Do stuff here
}

function calledfunc2(par1, par2, par3){
    // Do stuff here
}

mainfunc('calledfunc1','hello','bye');
mainfunc('calledfunc2','hello','bye','goodbye');

I apologize for my poor english, please ask me if you need some clarification about my (complicated) question.

Thanks in advance!

+15  A: 

Use the apply method of a function:-

function mainfunc (func){
    window[func].apply(null, Array.prototype.slice.call(arguments, 1));
}

Edit: It occurs to me that this would be much more useful with a slight tweak:-

function mainfunc (func){
    this[func].apply(this, Array.prototype.slice.call(arguments, 1));
}

This will work outside of the browser (this defaults to the global space). The use of call on mainfunc would also work:-

function target(a)
{
 alert(a)
}

var o = {suffix: " World",
  target : function(s) {
  alert(s + this.suffix);
 }
}

mainfunc("target", "Hello");

mainfunc.call(o, "target", "Hello");
AnthonyWJones
beat me to it :)https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Function/apply for more detailed documentation
cobbal
Thanks for that link cobbal! I'm reading about (and discovering) apply method.
ARemesal
This is the most adequate solution, but in arguments it'll included the name of function (arguments[0]='func'). I can generate another array with all the arguments except the very first one, and then use this new array, but, are there any other solution which not using other array?
ARemesal
ARemesal, see my answer for a solution (using the first argument as the function name)
J-P
@ARemesal: Oops yes forgot to slice off the func parameter from the arguments. See my edit.
AnthonyWJones
Yeah AnthonyWJones, it's equivalent to the solution by JimmyP. Thank you for your solution, I've discovered some great tricks :)
ARemesal
A: 

Couldn't you just pass the arguments array along?

function mainfunc (func){
    // remove the first argument containing the function name
    arguments.shift();
    window[func].apply(null, arguments);
}

function calledfunc1(args){
    // Do stuff here
}

function calledfunc2(args){
    // Do stuff here
}

mainfunc('calledfunc1','hello','bye');
mainfunc('calledfunc2','hello','bye','goodbye');
kkyy
No, you can't, since "arguments" is not an array but an array-like object and has no shift() member. You would need to copy the arguments 1 to arguments.length to a new array and pass that one to apply().
Ferdinand Beyer
+1  A: 

You could use .apply()

You need to specify a this... I guess you could use the this within mainfunc.

function mainfunc (func)
{
    var args = new Array();
    for (var i = 1; i < arguments.length; i++)
        args.push(arguments[i]);

    window[func].apply(this, args);
}
Greg
arguments is not an array and has no member slice().
Ferdinand Beyer
Oops, forgot about that - fixed
Greg
You don't need to convert 'arguments' to a REAL array. What's the point in this? It works perfectly without that step...
J-P
Not if you want to omit the first argument, which is the name of the function.
Ferdinand Beyer
Why not just use 'shift' then? (See my answer)
J-P
I didn't use shift because I didn't think [].xxx.call worked in IE but I can't reproduce the error now.
Greg
+2  A: 

Your code only works for global functions, ie. members of the window object. To use it with arbitrary functions, pass the function itself instead of its name as a string:

function dispatch(fn, args) {
    fn = (typeof fn == "function") ? fn : window[fn];  // Allow fn to be a function object or the name of a global function
    return fn.apply(this, args || []);  // args is optional, use an empty array by default
}

function f1() {}

function f2() {
    var f = function() {};
    dispatch(f, [1, 2, 3]);
}

dispatch(f1, ["foobar"]);
dispatch("f1", ["foobar"]);

f2();  // calls inner-function "f" in "f2"
dispatch("f", [1, 2, 3]);  // doesn't work since "f" is local in "f2"
Ferdinand Beyer
This works, but you should note it isn't what was asked for
Greg
+3  A: 

Here's what you need:

function mainfunc (){
    window[Array.prototype.shift.call(arguments)].apply(null, arguments);
}

The first argument is used as the function name and all of the remaining ones are used as arguments to the called function...

We're able to use the shift method to return and then delete the first value from the arguments array. Note that we've called it from the Array prototype since, strictly speaking, 'arguments' is not a real array and so doesn't inherit the shift method like a regular array would.


You can also call the shift method like this:

[].shift.call(arguments);
J-P
Thank you very much JimmyP, your solution is equivalent (after the edit) to the AnthonyWJones' one, and you gave me great tips and explanation about using Array.prototype and [].
ARemesal
Weird, I'm sure I tried this and got an error in IE
Greg