JavaScript variable number of arguments to function
Asked Answered
B

13

649

Is there a way to allow "unlimited" vars for a function in JavaScript?

Example:

load(var1, var2, var3, var4, var5, etc...)
load(var1)
Brackett answered 26/1, 2010 at 18:6 Comment(4)
possible duplicate of Is it possible to send a variable number of arguments to a JavaScript function?Spontaneous
related / possible duplicate of #4633625Frederic
@Frederic no, it's not. That question asks how to call a function with an arbitrary number of arguments with the arguments in an array. This asks how to handle such a call.Chasse
For easier searching, such a function is called a 'variadic function'.Peloquin
C
942

Sure, just use the arguments object.

function foo() {
  for (var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }
}
Coachwork answered 26/1, 2010 at 18:8 Comment(13)
Tnx. It is great for parsing Strings from android native code to javascript in a Webview.Puccoon
This solution worked best for me. Thanks. Further information on the arguments keyword HERE.Sabatier
arguments is a special "Array-like" object, which means it has has a length, but no other array functions. See developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… for more information, and this answer: https://mcmap.net/q/65020/-is-it-possible-to-get-all-arguments-of-a-function-as-single-object-inside-that-functionFrederic
Interestingly, the Array methods in Javascript have been defined in such a way that they work on any object that has a length property. This includes the arguments object. Knowing this, and that the method concat returns a copy of the 'array' it's called on, we can easily convert the arguments object to a real array like this: var args = [].concat.call(arguments). Some people prefer to use Array.prototype.concat.call instead, but I like the [], they are short and sweet!Sealy
This approach does NOT work well with arrow functions. You could use the approach of Ramast (below)Reticulation
Deprecated This feature has been removed from the Web standards. Though some browsers may still support it, it is in the process of being dropped. Avoid using it and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.Criollo
@DavidGatti It looks like using Function.arguments is deprecated, but not the vanilla arguments object. See developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… .Coachwork
What do you understand by vanilla? I still see a thumb down next to it.Criollo
@DavidGatti From the text below the thumb down: "This is deprecated as property of Function. Use the arguments object available within the function instead."Coachwork
@StijndeWitt: When I try what you suggest, I get a single-element array whose element is a hash with keys "0", "1", ...Chenille
Looks like you are adding the arguments object to the array. Make sure to use call to invoke the concat function. If you invoke it directly the object will be added. The trick with using call is that concat will be invoked with arguments as it's this. So it will clone the arguments object to a real array.Sealy
I wish I could do arguments.join().Radiocommunication
@YasirJan use [...arguments].join()Pentastyle
C
345

In (most) recent browsers, you can accept variable number of arguments with this syntax:

function my_log(...args) {
     // args is an Array
     console.log(args);
     // You can pass this array as parameters to another function
     console.log(...args);
}

Here's a small example:

function foo(x, ...args) {
  console.log(x, args, ...args, arguments);
}

foo('a', 'b', 'c', z='d')

=>

a
Array(3) [ "b", "c", "d" ]
b c d
Arguments
​    0: "a"
    ​1: "b"
    ​2: "c"
    ​3: "d"
    ​length: 4

Documentation and more examples here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters

Colorfast answered 20/8, 2016 at 4:46 Comment(6)
FYI it is called "the rest parameter syntax": developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Scuff
+1 This is elegant and clean solution. Especially suitable for passing through long list of parameters into another function call, and with possible that those variable parameters are at arbitrary position.Penumbra
Keep in mind that according to developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… it's not supported on IE.Abdulabdulla
Here's a table showing browser support - caniuse.com/#feat=rest-parametersNaamann
Such a nice answer!Bayle
Worth to note that z='d' syntax does NOT assign to an argument named z (as it would do, say, in Python), but instead passes an unnamed argument value 'd' and creates a local variable z with this same value as a side effect.Stereo
R
125

Another option is to pass in your arguments in a context object.

function load(context)
{
    // do whatever with context.name, context.address, etc
}

and use it like this

load({name:'Ken',address:'secret',unused:true})

This has the advantage that you can add as many named arguments as you want, and the function can use them (or not) as it sees fit.

Reify answered 26/1, 2010 at 18:34 Comment(4)
This would be better since it removes the coupling to argument order. Loosely coupled interfaces are good standard practice...Greatest
Sure, that's better in some cases. But let's say the individual arguments don't really relate to one another, or are all supposed to have equal meaning (like array elements). Then OP's way is best.Rondarondeau
This is also nice because if you want, you can build the context argument with code and pass it around before it gets used.Scoles
This solution worked best for me.Tess
G
53

I agree with Ken's answer as being the most dynamic and I like to take it a step further. If it's a function that you call multiple times with different arguments - I use Ken's design but then add default values:

function load(context) {

    var defaults = {
        parameter1: defaultValue1,
        parameter2: defaultValue2,
        ...
    };

    var context = extend(defaults, context);

    // do stuff
}

This way, if you have many parameters but don't necessarily need to set them with each call to the function, you can simply specify the non-defaults. For the extend method, you can use jQuery's extend method ($.extend()), craft your own or use the following:

function extend() {
    for (var i = 1; i < arguments.length; i++)
        for (var key in arguments[i])
            if (arguments[i].hasOwnProperty(key))
                arguments[0][key] = arguments[i][key];
    return arguments[0];
}

This will merge the context object with the defaults and fill in any undefined values in your object with the defaults.

Gosling answered 23/7, 2012 at 14:11 Comment(2)
+1. Nice trick. Saves a lot of boiler plate to have every parameter defined, default or otherwise.Closegrained
Underscore's _.defaults() method is a very nice alternative to merge specified and default arguments.Gosling
S
24

It is preferable to use rest parameter syntax as Ramast pointed out.

function (a, b, ...args) {}

I just want to add some nice property of the ...args argument

  1. It is an array, and not an object like arguments. This allows you to apply functions like map or sort directly.
  2. It does not include all parameters but only the one passed from it on. E.g. function (a, b, ...args) in this case args contains argument 3 to arguments.length
Soaring answered 2/12, 2016 at 15:40 Comment(0)
E
20

Yes, just like this :

function load()
{
  var var0 = arguments[0];
  var var1 = arguments[1];
}

load(1,2);
Ethnology answered 26/1, 2010 at 18:8 Comment(0)
K
19

As mentioned already, you can use the arguments object to retrieve a variable number of function parameters.

If you want to call another function with the same arguments, use apply. You can even add or remove arguments by converting arguments to an array. For example, this function inserts some text before logging to console:

log() {
    let args = Array.prototype.slice.call(arguments);
    args = ['MyObjectName', this.id_].concat(args);
    console.log.apply(console, args);
}
Kerrison answered 12/2, 2016 at 2:27 Comment(1)
nice solution to convert arguments to array. It was helpful for me today.Bilection
R
10

Although I generally agree that the named arguments approach is useful and flexible (unless you care about the order, in which case arguments is easiest), I do have concerns about the cost of the mbeasley approach (using defaults and extends). This is an extreme amount of cost to take for pulling default values. First, the defaults are defined inside the function, so they are repopulated on every call. Second, you can easily read out the named values and set the defaults at the same time using ||. There is no need to create and merge yet another new object to get this information.

function load(context) {
   var parameter1 = context.parameter1 || defaultValue1,
       parameter2 = context.parameter2 || defaultValue2;

   // do stuff
}

This is roughly the same amount of code (maybe slightly more), but should be a fraction of the runtime cost.

Ringsmuth answered 7/2, 2013 at 21:59 Comment(1)
Agreed, although the harm depends on the type of value or default itself. Otherwise, (parameter1=context.parameter1)===undefined && (parameter1=defaultValue1) or for less code volume a small helper function like: function def(providedValue, default) {return providedValue !== undefined ? providedValue : defaultValue;} var parameter1 = def(context.parameter1, defaultValue1) provide alternate patterns. However, my point still stands: creating extra objects for every function invocation and running expensive loops to set a couple of default values is a crazy amount of overhead.Ringsmuth
G
10

While @roufamatic did show use of the arguments keyword and @Ken showed a great example of an object for usage I feel neither truly addressed what is going on in this instance and may confuse future readers or instill a bad practice as not explicitly stating a function/method is intended to take a variable amount of arguments/parameters.

function varyArg () {
    return arguments[0] + arguments[1];
}

When another developer is looking through your code is it very easy to assume this function does not take parameters. Especially if that developer is not privy to the arguments keyword. Because of this it is a good idea to follow a style guideline and be consistent. I will be using Google's for all examples.

Let's explicitly state the same function has variable parameters:

function varyArg (var_args) {
    return arguments[0] + arguments[1];
}

Object parameter VS var_args

There may be times when an object is needed as it is the only approved and considered best practice method of an data map. Associative arrays are frowned upon and discouraged.

SIDENOTE: The arguments keyword actually returns back an object using numbers as the key. The prototypal inheritance is also the object family. See end of answer for proper array usage in JS

In this case we can explicitly state this also. Note: this naming convention is not provided by Google but is an example of explicit declaration of a param's type. This is important if you are looking to create a more strict typed pattern in your code.

function varyArg (args_obj) {
    return args_obj.name+" "+args_obj.weight;
}
varyArg({name: "Brian", weight: 150});

Which one to choose?

This depends on your function's and program's needs. If for instance you are simply looking to return a value base on an iterative process across all arguments passed then most certainly stick with the arguments keyword. If you need definition to your arguments and mapping of the data then the object method is the way to go. Let's look at two examples and then we're done!

Arguments usage

function sumOfAll (var_args) {
    return arguments.reduce(function(a, b) {
        return a + b;
    }, 0);
}
sumOfAll(1,2,3); // returns 6

Object usage

function myObjArgs(args_obj) {
    // MAKE SURE ARGUMENT IS AN OBJECT OR ELSE RETURN
    if (typeof args_obj !== "object") {
        return "Arguments passed must be in object form!";
    }

    return "Hello "+args_obj.name+" I see you're "+args_obj.age+" years old.";
}
myObjArgs({name: "Brian", age: 31}); // returns 'Hello Brian I see you're 31 years old

Accessing an array instead of an object ("...args" The rest parameter)

As mentioned up top of the answer the arguments keyword actually returns an object. Because of this any method you want to use for an array will have to be called. An example of this:

Array.prototype.map.call(arguments, function (val, idx, arr) {});

To avoid this use the rest parameter:

function varyArgArr (...var_args) {
    return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5
Gallia answered 24/2, 2017 at 3:47 Comment(0)
I
7

Use the arguments object when inside the function to have access to all arguments passed in.

Indiscerptible answered 26/1, 2010 at 18:8 Comment(0)
P
6

Be aware that passing an Object with named properties as Ken suggested adds the cost of allocating and releasing the temporary object to every call. Passing normal arguments by value or reference will generally be the most efficient. For many applications though the performance is not critical but for some it can be.

Premolar answered 6/8, 2014 at 20:25 Comment(0)
A
3

REST PARAMETERS

refer Rest Parameters MDN

function addition(...numbers){
    var sum=0;
    for(var i=0;i<numbers.length;i++){
        sum+=numbers[i];
    }
    return sum;
 }

  console.log(addition(1,2,3));
Aliciaalick answered 11/7, 2023 at 0:56 Comment(0)
P
0

Use array and then you can use how many parameters you need. For example, calculate the average of the number elements of an array:

function fncAverage(sample) {
    var lenghtSample = sample.length;
    var elementsSum = 0;
    for (var i = 0; i < lenghtSample; i++) {
        elementsSum = Number(elementsSum) + Number(sample[i]);
    }
    average = elementsSum / lenghtSample
    return (average);
}

console.log(fncAverage([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); // results 5.5

let mySample = [10, 20, 30, 40];
console.log(fncAverage(mySample)); // results 25

//try your own arrays of numbers
Piano answered 8/12, 2021 at 18:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.