641

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

Example:

load(var1, var2, var3, var4, var5, etc...)
load(var1)
bluish
  • 26,356
  • 27
  • 122
  • 180
Timo
  • 7,195
  • 7
  • 24
  • 25
  • possible duplicate of [Is it possible to send a variable number of arguments to a JavaScript function?](http://stackoverflow.com/questions/1959040/is-it-possible-to-send-a-variable-number-of-arguments-to-a-javascript-function) – Nakilon Feb 07 '13 at 05:32
  • related / possible duplicate of http://stackoverflow.com/questions/4633125/is-it-possible-to-get-all-arguments-of-a-function-as-single-object-inside-that-f/13145228 – Luke Apr 22 '15 at 01:33
  • 1
    @Luke 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. – Scruffy Jan 02 '17 at 04:45
  • 7
    For easier searching, such a function is called a 'variadic function'. – gschenk Jan 22 '20 at 21:40

13 Answers13

931

Sure, just use the arguments object.

function foo() {
  for (var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }
}
roufamatic
  • 18,187
  • 7
  • 57
  • 86
  • 1
    Tnx. It is great for parsing Strings from android native code to javascript in a Webview. – Johan Hoeksma Aug 31 '13 at 16:06
  • 5
    This solution worked best for me. Thanks. Further information on the arguments keyword [HERE](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments). – User2 Apr 30 '14 at 09:18
  • 39
    `arguments` is a special "Array-like" object, which means it has has a length, but no other array functions. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments for more information, and this answer: http://stackoverflow.com/a/13145228/1766230 – Luke Apr 22 '15 at 01:35
  • 7
    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! – Stijn de Witt Nov 11 '16 at 14:39
  • This approach does NOT work well with [arrow functions](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_binding_of_arguments). You could use the approach of Ramast (below) – anhldbk Jul 21 '17 at 03:04
  • 2
    **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. – David Gatti Sep 02 '17 at 12:16
  • 2
    @DavidGatti It looks like using `Function.arguments` is deprecated, but not the vanilla `arguments` object. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function . – roufamatic Sep 05 '17 at 22:22
  • What do you understand by vanilla? I still see a thumb down next to it. – David Gatti Sep 06 '17 at 14:01
  • 3
    @DavidGatti From the text below the thumb down: "This is deprecated as property of Function. Use the arguments object available within the function instead." – roufamatic Sep 07 '17 at 16:48
  • @StijndeWitt: When I try what you suggest, I get a single-element array whose element is a hash with keys "0", "1", ... –  Jun 23 '18 at 18:49
  • 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. – Stijn de Witt Jun 25 '18 at 14:06
  • I wish I could do arguments.join(). – Yasir Jan Apr 11 '19 at 10:59
  • 4
    @YasirJan use ```[...arguments].join()``` – Willian Andrade Jul 22 '19 at 00:05
338

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

Brian Burns
  • 20,575
  • 8
  • 83
  • 77
Ramast
  • 7,157
  • 3
  • 32
  • 32
  • 42
    FYI it is called "the rest parameter syntax": https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters – tanguy_k Nov 16 '16 at 10:22
  • 4
    +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. – haxpor Sep 06 '17 at 07:55
  • 4
    Keep in mind that according to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters it's not supported on IE. – Roee Gavirel Dec 31 '18 at 10:23
  • 3
    Here's a table showing browser support - https://caniuse.com/#feat=rest-parameters – Brian Burns Apr 10 '19 at 09:15
  • 1
    Such a nice answer! – Ashish Aug 13 '19 at 12:17
  • 1
    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**. – Marcin Wojnarski Sep 07 '21 at 13:08
124

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.

Ken
  • 77,016
  • 30
  • 84
  • 101
  • 4
    This would be better since it removes the coupling to argument order. Loosely coupled interfaces are good standard practice... – Jonas Schubert Erlandsson Jan 29 '13 at 20:05
  • 10
    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. – rvighne Dec 08 '13 at 23:05
  • 1
    This is also nice because if you want, you can build the `context` argument with code and pass it around before it gets used. – Nate C-K Jan 29 '14 at 08:17
52

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.

mbeasley
  • 4,864
  • 5
  • 27
  • 39
  • 3
    +1. Nice trick. Saves a lot of boiler plate to have every parameter defined, default or otherwise. – Neil Sep 17 '12 at 10:23
  • 1
    [Underscore's](http://underscorejs.org/#defaults) `_.defaults()` method is a very nice alternative to merge specified and default arguments. – mbeasley Feb 14 '13 at 13:33
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
Nicola Pedretti
  • 4,831
  • 3
  • 36
  • 42
20

Yes, just like this :

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

load(1,2);
Canavar
  • 47,715
  • 17
  • 91
  • 122
18

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);
}
Peter Tseng
  • 13,613
  • 4
  • 67
  • 57
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.

mcurland
  • 121
  • 1
  • 5
  • 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. – mcurland Aug 28 '14 at 23:02
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
Brian Ellis
  • 2,271
  • 2
  • 17
  • 24
7

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

bluish
  • 26,356
  • 27
  • 122
  • 180
nickytonline
  • 6,855
  • 6
  • 42
  • 76
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.

phbcanada
  • 79
  • 1
  • 2
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
Marcus Arthur
  • 11
  • 1
  • 2
0

REST PARAMETERS

refer Rest Parameters MDN

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

  console.log(sum);
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 12 '23 at 18:54