简体   繁体   中英

Call a function with a variable number of arguments in JavaScript (similar to call())

I'm familiar with the way call() , which you can pass a variable number of arguments that will be loaded into a function's parameters when called. I'm trying to do something related where I recurse through nested set objects in RaphaelJS with forEach (analogous to jQuery's each ), determine whether the child element is another set, and apply a function with a variable number of arguments if not. I want to make it generic so that I can apply any function, but make the functions that I pass have simple parameter constructors without having to access the arguments property of the function.

function recursiveFncApply(set, fnc, args) {
    set.forEach(function(item) {
        if (item.type == 'set') {
            recurseApplyFncToSets(item, fnc, args);
        } else {
            fnc(item, args);
        }
    });
}

function translateOperation(element, operation, x, y)
    // do stuff to element with operation, x, and y args without accessing
    // accessing arguments property
}

recursiveFncApply(passedSet, translateOperation, [arg1, [arg2, ...]]);

I want to do this so that I can use multiple functions without having to repeat myself with code that determines arguments and properly assigns them before usage. I'm not sure whether there's some kind of functionality or language utility that I'm missing that would enable me to do this, or somehow to programmatically "construct" a function call from the remaining arguments passed to recursiveFncApply. Is this possible in JavaScript?

Clarification: I want to pass a variable number of arguments to my recursive function that will be passed to any function that I want to be applied to the contents of the sets my recursive function is working on. So I want to be able to make recursiveFncApply work generically with any function while still using an argument structure that works like a function being executed via call().

Say I have another function in addition to translateOperation :

function anotherFunction(element, differentArg) {
    // do something with one argument
}

Ideally I could then use my recursiveFncApply in this way:

recursiveFncApply(passedSet, translateOperation, operation, x, y);
recursiveFncApply(passedSet, anotherFunction, singleArg);

As well as this way:

recursiveFncApply(passedSet, anotherFunction, singleArg);

I believe that this is similar to how call() works in that I could do:

anotherFunction.call(this, element, differentArg);

.. without having to change the structure of anotherFunction to sort out the arguments property, or pass an object/array.

It turns out that Felix King had the right idea/was the closest. I found a direct answer to my question as soon as I realized what I was actually trying to do, which is pass forward arguments from function to function (found the answer here ). So I got this to work with this code:

function recursiveSetFncApply(set, fnc/*, variable */) {
    var me = this;
    var parentArgs = arguments;
    set.forEach(function(element) {
        if (element.type == 'set') {
            parentArgs[0] = element;
            me._recursiveSetFncApply.apply(me, parentArgs);
        } else {
            // Generate args from optional arguments and pass forward; put element in args at front
            var args = Array.prototype.slice.call(parentArgs, 2);
            args.unshift(element);
            fnc.apply(element, args);
        }
    });
}

I make a reference to arguments with parentArgs because I need to update the set property in arguments before I pass it forward to the next recursive loop, otherwise I hit an infinite loop because it hasn't updated at all because it's using the original arguments set. I was under the impression that apply() will not actually pass forward arguments, but simply pop an array into the new function that you have to access by index--this isn't the case. When I used apply() on translateElementOperation , I had all the arguments I needed in their exact places. Here's the updated function I ran through the recursive apply:

function translateElementOperation(element, operation, x, y) {
    var currentPath = element.attr('path');
    translatedPath  = Raphael.transformPath(currentPath, [operation, x, y]);
    element.attr('path', translatedPath);
}

Thanks for the help, everyone!

Use .apply instead of .call

functionName.apply(element, [any, number, of, variables, ...]);

// instead of this
functionName.apply(element, set, of, variables, ...);

This is more useful like so:

var fnVars = [];// fill this anyway you want.

functionName.apply(element, fnVars);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM