161

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 the 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 ifelse)?

function mainfunc(func){
    if(arguments.length == 3)
        window[func](arguments[1], arguments[2]);
    else if(arguments.length == 4)
        window[func](arguments[1], arguments[2], arguments[3]);
    else if(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');
Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
ARemesal
  • 2,923
  • 5
  • 24
  • 23
  • 2
    The PHP equivalent for `apply()` is `call_user_func_array()`. The http://phpjs.org/functions/call_user_func_array solution also uses it. – powtac Aug 25 '11 at 14:59

10 Answers10

197

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");
Zach Olivare
  • 3,805
  • 3
  • 32
  • 45
AnthonyWJones
  • 187,081
  • 35
  • 232
  • 306
  • 7
    beat me to it :) https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Function/apply for more detailed documentation – cobbal Mar 24 '09 at 09:57
  • 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 Mar 24 '09 at 11:18
  • ARemesal, see my answer for a solution (using the first argument as the function name) – James Mar 24 '09 at 12:12
  • @ARemesal: Oops yes forgot to slice off the func parameter from the arguments. See my edit. – AnthonyWJones Mar 24 '09 at 13:18
  • Yeah AnthonyWJones, it's equivalent to the solution by JimmyP. Thank you for your solution, I've discovered some great tricks :) – ARemesal Mar 24 '09 at 14:06
  • I ran across this about 8 years ago, and I used an eval to call the function, fugly, I know. Good thing we realized it and changed it to use apply about 6 months later. – Ruan Mendes Nov 03 '10 at 20:23
  • may i know why u use `window[func].apply` instead of `func.apply` – slier Jun 21 '13 at 20:23
  • `Array.prototype.slice.call(arguments, 1)` hit me at first but then, I found a similar reference at [mozilla](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fapply). Possible implementation of this theory: try to call user-functions from event-handling ones! – centurian Aug 23 '13 at 07:46
24

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
  • 64,979
  • 15
  • 154
  • 145
15

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
  • 316,276
  • 54
  • 369
  • 333
12

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);
Daniel Naab
  • 22,690
  • 8
  • 54
  • 55
James
  • 109,676
  • 31
  • 162
  • 175
  • 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 Mar 24 '09 at 14:09
  • Weird, I'm sure I tried this and got an error in IE – Greg Mar 24 '09 at 14:15
8

The simplest way might be:

var func='myDynamicFunction_'+myHandler;
var arg1 = 100, arg2 = 'abc';

window[func].apply(null,[arg1, arg2]);

Assuming, that target function is already attached to a "window" object.

lubosdz
  • 4,210
  • 2
  • 29
  • 43
3

If you want to pass with "arguments" a few others, you have to create the array of all arguments together, i.e. like this:

var Log = {
    log: function() {
        var args = ['myarg here'];
        for(i=0; i<arguments.length; i++) args = args.concat(arguments[i]);
        console.log.apply(this, args);
    }
}
0

Now I'm using this:

Dialoglar.Confirm = function (_title, _question, callback_OK) {
    var confirmArguments = arguments;
    bootbox.dialog({
        title: "<b>" + _title + "</b>",
        message: _question,
        buttons: {
            success: {
                label: "OK",
                className: "btn-success",
                callback: function () {
                    if (typeof(callback_OK) == "function") {                            callback_OK.apply(this,Array.prototype.slice.call(confirmArguments, 3));
                    }
                }
            },
            danger: {
                label: "Cancel",
                className: "btn-danger",
                callback: function () {
                    $(this).hide();
                }
            }
        }
    });
};
uzay95
  • 16,052
  • 31
  • 116
  • 182
-2
function a(a, b) {
    return a + b
};

function call_a() {
    return a.apply(a, Array.prototype.slice.call(arguments, 0));
}

console.log(call_a(1, 2))

console: 3

-4

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
  • 12,214
  • 3
  • 32
  • 27
  • 3
    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 Mar 24 '09 at 10:07
-7

In case somebody is still looking for dynamic function call with dynamic parameters -

callFunction("aaa('hello', 'world')");

    function callFunction(func) {
                try
                {
                    eval(func);
                }
                catch (e)
                { }
            }
    function aaa(a, b) {
                alert(a + ' ' + b);
            }
kma1975
  • 7
  • 3