2

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.

Way Spurr-Chen
  • 405
  • 2
  • 9
  • possible duplicate of http://stackoverflow.com/questions/2141520/javascript-variable-number-of-arguments-to-function – Phillip Kinkade Dec 19 '13 at 19:27
  • 1
    Pass an object {'a':'something','b':'something else'} instead of arguments. – Diodeus - James MacFarlane Dec 19 '13 at 19:27
  • Maybe I misunderstand your problem, but have you considered using an array of arguments and using [`fnc.apply`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)? – apsillers Dec 19 '13 at 19:29
  • 5
    If I understand you correctly, you are looking for `fnc.apply(this, [item].concat(args))` – Felix Kling Dec 19 '13 at 19:29
  • Yes; I know that there are _other_ ways to do this, I'm primarily curious if there's any way to do this with JavaScript in a way similar how `call()` works. Sort of like.. the inverse of `call()`? I would say that the question I'm asking is the opposite of http://stackoverflow.com/questions/2141520/javascript-variable-number-of-arguments-to-function. – Way Spurr-Chen Dec 19 '13 at 19:30
  • I don't think I understand the question. So, you want to pass a variable number of arguments to `translateOperation`? I'd try @Diodeus' suggestion of passing *one* object as the only parameter. http://stackoverflow.com/a/2141672/206403 – gen_Eric Dec 19 '13 at 19:33
  • Could you provide sample input/expected output? –  Dec 19 '13 at 19:34
  • Sorry, I edited the original code example to be more clear. 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()`. – Way Spurr-Chen Dec 19 '13 at 19:35
  • 1
    It sounds like you want [ES6's "rest parameter"](http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html) argument (as in "...and the rest"). You can simulate this behavior in ES5, but you need to use `arguments`. – apsillers Dec 19 '13 at 19:37
  • That looks like the kind of thing I'm imagining! Ah, well. I was just curious--I guess this kind of construct isn't possible/logical in JavaScript. Thanks, everyone. – Way Spurr-Chen Dec 19 '13 at 19:41

2 Answers2

0

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!

Community
  • 1
  • 1
Way Spurr-Chen
  • 405
  • 2
  • 9
0

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);
Kevin Jantzer
  • 9,215
  • 2
  • 28
  • 52