Forum Moderators: open
basically i need to write a function that accepts a variable number of arguments, no problem..
function foo(){
for(var i in arguments){
//do something useful
}
}
but ....
how can I create a function call to foo() with a variable number of arguments...
sometimes call foo(arg1, arg2, arg3)
and sometimes foo(arg1,arg2,arg3,arg4, arg5)
and have the parameters NOTjust clumped into one array like:
var a = [1,2,3,4,5];
foo(a);
I hope thats clear enough. Thanks for any help in advance
-Greg
how can I create a function call to foo() with a variable number of arguments...
sometimes call foo(arg1, arg2, arg3)
and sometimes foo(arg1,arg2,arg3,arg4, arg5)
Just as you have written it. Just call foo(arg1, arg2, arg3) or foo(arg1,arg2,arg3,arg4, arg5). You don't need to do anything special, just call the function with the desired arguments.
I knew I explained it poorly :S
Basically I want to build an array of parameters, and then convert it into parameters for a function call.
I have just written something that gets the job done, but I would like to know a way that can avoid using eval().
Anyway I'm sure my code will explain where I'm trying to get to better then I can:
/* funcName = name of function to call(string) */
/* argArray = array of arguments to send to the function (array) */
function callFunctionWithArguments(funcName, argArray){
var callstr = funcName + '(';
for(var i = 0; i < argArray.length; ++i){
callstr += (typeof(argArray[i]) == 'string')?argArray[i] :'argArray['+i+']';
if(typeof(argArray[i+1])!= 'undefined') callstr += ',';
}
callstr += ')';
eval(callstr);
}
/* a usage example being.... */
function showArgs(){alert('length: ' + arguments.length + '\narg0.tree: '+ arguments[0].tree);}
window.onload = function(){
var myobj = {tree:'spruce',color:'blue'};
var a = [myobj,1,2,3,4,5];
callFunctionWithArguments('showArgs', a);
}
any ideas?
onClick="callFunc(this,'tree:spruce,color:blue,size:3-ft');
function callFunc(obj,paramlist) {
params = paramlist.split(',');
for (i=0;i<params.length;i++) {
pairs = params[i].split(':');
alert('called from ' + obj.id + ' key ' + pairs[0] + ' value ' + pairs[1]);
}
}
If you want to covert some values to spaces, change the dash in 3-ft to a + and use a regexp to swap it out for a space. You want to use a + because that will easily convert to a query string if you use it later.
<script type="text/javascript">
function foo() {
var args = arguments;
alert(args.length + " actual parameters");
// If only 1 parameter was passed in, and that
// parameter contains an array, treat that array
// as the arguments array.
// Check to see if the first argument contains
// the "concat" method. This is just a test to
// see if it's an array. There may be better ways
// to find this info, this is just quick and dirty
// proof of concept.
if( args.length == 1 && args[0].concat )
{
args = args[0];
}
alert(args.length + " interpreted parameters");
}
var argArr = ['a','b','c'];
foo(argArr);
foo('z','y','x');
</script>
this is the same reason I want to avoid firing up the regular expression engine if I can. (Actually, I'm not exactly sure the performance hit in JavaScript, I know it hurts in server side scripting like Perl, PHP, ect.)
OK, the exact usage can clarify my reasoning I think
Anyway I made a controller object that will handle content area requests on a page, and I'd like to have the sub components register actions with the controller, and pass it a function that will be the designated handler. the problem arises in that when I go to trigger the action I'd like to use the same function to direct all actions by a call something like:
ctrlObj.triggerAction(actionName, paramsArray);
but then I have a function Reference stored for that action, and I'll need to convert the paramsArray into actual parameters to pass to the function.
//example:
function showTree(type, color, height){/* something useful here */
//then register the action with the controller as
ctrlObj.registerAction('getmeatree', 'showTree');
//then later I can request an action by
ctrlObj.triggerAction('getmeatree',['spruce','blue','3-ft']);
//and the controller will convert it into a call like:
showtree('spruce', 'blue','3-ft');
this will remove the dependency from whatever calls the triggerAction() from having to know what the function is, allowing me to change the handler at any time, and not having to worry about what the handling function is looking for. It will reduce my reusability I think if I rely on a handler function to check for an array as the first parameter.
(edit)
It also will allow me to build a parameter list as I go along, pushing parms onto the stack as it goes from function to function.
If I converted the parameter list to a string, I would have to rely on all the parameters that aren't scalers being in the global scope. If I can pass the actual object, it will work no matter what.
Am I overthinking this?
[edited by: s_mk at 8:54 pm (utc) on Feb. 22, 2007]
Why don't you want to use eval()?
Honestly, I've actually forgotten all the IMPORTANT reasons for avoiding eval. :-) In other languages, it actually presents security exploits. In Javascript, eval leads to bad habits and difficult to read code, it's major abuse manifesting itself in lots of quoting and + ing to build a text string to access an object that can be better done in other ways. It's also memory intensive and it's results are not cached (or so I have read,) so has a reputaton for taking up more memory than required to Do a Thing.
Eval is best used to evaluate strings meant to be interpreted as numeric expressions.
What I remember best is this: if you're pondering using eval, there is almost always a better and more appropriate way to do what you're doing.
Special thanks to BernardMarx whom we haven't seen much of late . . .
switch( arguments.length ) {
case 1:
func(arguments[0]);
break;
case 2:
func(arguments[0],arguments[1]);
break;
// etc., etc.
}
Build an array and pass the array as the sole parameter. Essentially, that's what javascript does anyway.
You know, he's certainly got a point there. Give it a try. I'll bet it works.
Remember that every function has an"arguments" local variable (used to be "arguments" property, but that has been deprecated [developer.mozilla.org]).
Build an array and pass the array as the sole parameter. Essentially, that's what javascript does anyway.
no it doesn't....
proof:
function showArgs(){alert(arguments.length);}
showArgs([1,2,3,4,5]); //outputs 1
if that were true, arguments.length should be 5
again I would have to rely on every function passed to the controller to be configured to look for an array as its argument and split it up. At least the using eval() technique has no outside dependencies
Remember that every function has an"arguments" local variable (used to be "arguments" property, but that has been deprecated).
yes, but it's only created after the function call is made.
I only wish there was a way to create a function call, but not execute it until you've loaded a parameters property.
*sigh*
My mother used to tell me to sh** in one hand and wish in the other and see which one fills up first :)
Thanks for all the suggestions!
-Greg
again I would have to rely on every function passed to the controller to be configured to look for an array as its argument and split it up.
You said
Basically I want to build an array of parameters, and then convert it into parameters for a function call.
Kaled.
It is pointless converting an array into parameters when all you have to do is pass it as an array. Given that, internally, javascript passes parameters as a array
Sorry, I thought you were telling me to send it an array as its parameter.
what you describe is exactly what I'm trying to accomplish, I just haven't been able to figure out how to send an array to a function and have it end up as the arguments object. Can you give me an example on how I can do this?
Thanks for the help :)
-Greg.
Did you not understand my reply about using a switch statement?
Of course I understood perfectly, but if the number of parameters is not known, I'm only limiting the functionality by running through the switch. In this case that's not acceptable. In fact, I would tend to avoid that as a rule.
As far as passing an array and relying on the function to sort it out. Unacceptable as well, then I can't plug in any previously written, or third party functionality without rewriting it.
I think I got all I need from this dead horse. I really do appreciate the help and advise.
Just a question though, if you pass an array of arguments as an argument, aren't you wasting CPU time as well by having the arguments array created, and then making it work with another array? 2x the storage as well right? you have the 1. arguments array, and 2. the array of arguments...
thanks again,
-Greg
function privateFoo( arg1, arg2, arg3 ){}
This existing function takes 3 parameters. s_mk wants to keep the implementation details of this function hidden from the user, and does not want to change that function to only take a single parameter (that happens to be an array).
Instead, the user would call s_mk's method, passing it an array to be treated as parameters. The implementation details would remain hidden from the user. For example:
foobar([arg1, arg2, arg3])
This method would then call the privateFoo method, passing the 3 arguments. However, if s_mk later changed the implementation details to call some other method:
function privateBar( arg1, arg2 )
the user still calls foobar([arg1, arg2, arg3]), and the implementation details remain hidden.
My initial thought is that you're either going to need to use eval somehow, or you'll need to use a switch statement explicitly call the private method with each of the individual arguments.
ok, now this thread is starting to flame....
Note, my comment was in no way meant as a flame.
Did you not understand my reply about using a switch statement?
Of course I understood perfectly, but if the number of parameters is not known, I'm only limiting the functionality by running through the switch. In this case that's not acceptable. In fact, I would tend to avoid that as a rule.
And I agree with that as a general rule. But in this particular case, I think it's your only other option besides using an eval. Like I said, you can probably safely handle this using switch with 9 or 10 cases.
In any case, good luck.
function plus(a,b){return a+b}
function curry(f,x){
return function(){
var arg=[x].concat(Array.prototype.slice.apply(arguments));
return f.apply({},arg);
};
}
alert(curry(plus,2)(3));
I think this is exactly what you're trying to do. Your main function takes an array as one of the arguments. So perhaps you could call the apply() method of Function to invoke the function with your array of arguments?
functionName.apply( {} , argArray );
beeeutiful. I figured with javascript, where there was a will there way a way.
I'm a bit confused at how the parenthesis work. in the call.
curry(plus,2)(3)
how does that extra set of parens work? It's not in the arguments array just seems to appear when the slice.apply() is called
puzzled.
Thanks a million though.
-Greg
function plus(a,b){return a+b}
function curry(f,x){
return function(){
var arg=[x].concat(Array.prototype.slice.apply(arguments));
return f.apply({},arg);
};
}
alert(curry(plus,2)(3));
The curry function takes 2 arguments and returns an anonymous function:
function curry(f,x){
return function(){
...
};
}
The second set of parenthesis (containing 3 in the example) is then invocating that returned function with the passed in argument.
So curry(plus,2)(3) is the same as doing this:
function(){
var arg=[2];
arg.concat(Array.prototype.slice.apply(arguments));
return plus.apply({},arg);
}(3);
arg is an array which starts out with a single argument (2 in the example above), and then has other values concatinated to it (in this case, the value 3). It then returns plus.apply({},[2,3]), where {} represents the object to which plus should be applied (if the plus function had a reference to "this", this would point to this object). In the example above, an empty object is passed.