Número variable de argumentos de JavaScript para funcionar

531

¿Hay alguna manera de permitir variables "ilimitadas" para una función en JavaScript?

Ejemplo:

load(var1, var2, var3, var4, var5, etc...)
load(var1)
Timo
fuente
duplicado relacionado / posible de stackoverflow.com/questions/4633125/…
Lucas
1
@Luke no, no lo es. Esa pregunta pregunta cómo llamar a una función con un número arbitrario de argumentos con los argumentos en una matriz. Esto pregunta cómo manejar tal llamada.
Desaliñado
1
Para una búsqueda más fácil, dicha función se denomina 'función variable'.
gschenk

Respuestas:

814

Claro, solo usa el argumentsobjeto.

function foo() {
  for (var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }
}
roufamatic
fuente
1
Tnx. Es ideal para analizar cadenas desde el código nativo de Android a JavaScript en una vista web.
Johan Hoeksma
44
Esta solución funcionó mejor para mí. Gracias. Más información sobre la palabra clave argumentos AQUÍ .
Usuario2
26
argumentses un objeto especial "tipo matriz", lo que significa que tiene una longitud, pero ninguna otra función de matriz. Consulte developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… para obtener más información, y esta respuesta: stackoverflow.com/a/13145228/1766230
Luke
44
Curiosamente, los métodos de matriz en Javascript se han definido de tal manera que funcionan en cualquier objeto que tenga una lengthpropiedad. Esto incluye el argumentsobjeto. Sabiendo esto, y que el método concatdevuelve una copia de la 'matriz' se llama en adelante, podemos fácilmente convertir el argumentsobjeto a una matriz real como esto: var args = [].concat.call(arguments). Algunas personas prefieren usar Array.prototype.concat.callen su lugar, pero me gusta [], ¡son cortas y dulces!
Stijn de Witt
2
@YasirJan use[...arguments].join()
Willian D. Andrade
179

En (la mayoría) de los navegadores recientes, puede aceptar un número variable de argumentos con esta sintaxis:

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

Aquí hay un pequeño ejemplo:

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

Documentación y más ejemplos aquí: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters

Ramast
fuente
27
FYI se llama "la sintaxis del parámetro de reposo": developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
tanguy_k
44
+1 Esta es una solución elegante y limpia. Especialmente adecuado para pasar una larga lista de parámetros a otra llamada de función, y con la posibilidad de que esos parámetros variables estén en una posición arbitraria.
haxpor
3
Tenga en cuenta que según developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… no es compatible con IE.
Roee Gavirel
2
Aquí hay una tabla que muestra el soporte del navegador - caniuse.com/#feat=rest-parameters
Brian Burns
1
¡Qué buena respuesta!
Ashish
113

Otra opción es pasar sus argumentos en un objeto de contexto.

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

y úsalo así

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

Esto tiene la ventaja de que puede agregar tantos argumentos con nombre como desee, y la función puede usarlos (o no) como mejor le parezca.

Conocer
fuente
44
Esto sería mejor ya que elimina el acoplamiento al orden de los argumentos. Las interfaces libremente acopladas son una buena práctica estándar ...
Jonas Schubert Erlandsson
99
Claro, eso es mejor en algunos casos. Pero digamos que los argumentos individuales realmente no se relacionan entre sí, o se supone que todos tienen el mismo significado (como elementos de matriz). Entonces el camino de OP es el mejor.
rvighne
1
Esto también es bueno porque si lo desea, puede construir el contextargumento con código y pasarlo antes de que se use.
Nate CK
46

Estoy de acuerdo con la respuesta de Ken como la más dinámica y me gusta ir un paso más allá. Si es una función que llama varias veces con diferentes argumentos, uso el diseño de Ken pero luego agrego valores predeterminados:

function load(context) {

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

    var context = extend(defaults, context);

    // do stuff
}

De esta manera, si tiene muchos parámetros pero no necesariamente necesita establecerlos con cada llamada a la función, simplemente puede especificar los valores no predeterminados. Para el método extendido, puede usar el método extendido de jQuery ( $.extend()), crear el suyo propio o usar lo siguiente:

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];
}

Esto fusionará el objeto de contexto con los valores predeterminados y completará cualquier valor indefinido en su objeto con los valores predeterminados.

mbeasley
fuente
3
+1. Buen truco. Ahorra mucha placa de caldera para tener todos los parámetros definidos, por defecto o de otra manera.
Neil
1
El _.defaults() método de subrayado es una muy buena alternativa para fusionar argumentos especificados y predeterminados.
mbeasley
18

Sí, así como así:

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

load(1,2);
Canavar
fuente
18

Es preferible utilizar la sintaxis del parámetro rest como señaló Ramast.

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

Solo quiero agregar alguna buena propiedad del argumento ... args

  1. Es una matriz, y no un objeto como los argumentos. Esto le permite aplicar funciones como mapear u ordenar directamente.
  2. No incluye todos los parámetros, sino solo el que se transmitió. Por ejemplo, la función (a, b, ... args) en este caso args contiene el argumento 3 a argumentos.length
Nicola Pedretti
fuente
15

Como ya se mencionó, puede usar el arguments objeto para recuperar un número variable de parámetros de función.

Si desea llamar a otra función con los mismos argumentos, use apply. Incluso puede agregar o eliminar argumentos mediante la conversión argumentsa una matriz. Por ejemplo, esta función inserta texto antes de iniciar sesión en la consola:

log() {
    let args = Array.prototype.slice.call(arguments);
    args = ['MyObjectName', this.id_].concat(args);
    console.log.apply(console, args);
}
Peter Tseng
fuente
Buena solución para convertir argumentos a matriz. Fue útil para mí hoy.
Dmytro Medvid
8

Aunque generalmente estoy de acuerdo en que el enfoque de los argumentos nombrados es útil y flexible (a menos que le importe el orden, en cuyo caso los argumentos son más fáciles), me preocupa el costo del enfoque de mbeasley (uso de valores predeterminados y ampliaciones). Esta es una cantidad extrema de costo a tomar para extraer valores predeterminados. Primero, los valores predeterminados se definen dentro de la función, por lo que se vuelven a llenar en cada llamada. En segundo lugar, puede leer fácilmente los valores nombrados y establecer los valores predeterminados al mismo tiempo usando ||. No es necesario crear y fusionar otro objeto nuevo para obtener esta información.

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

   // do stuff
}

Esto es aproximadamente la misma cantidad de código (quizás un poco más), pero debería ser una fracción del costo de tiempo de ejecución.

mcurland
fuente
De acuerdo, aunque el daño depende del tipo de valor o incumplimiento en sí. De lo contrario, (parameter1=context.parameter1)===undefined && (parameter1=defaultValue1)o por menos volumen de código, una pequeña función auxiliar como: function def(providedValue, default) {return providedValue !== undefined ? providedValue : defaultValue;} var parameter1 = def(context.parameter1, defaultValue1)proporcionar patrones alternativos. Sin embargo, mi punto sigue en pie: crear objetos adicionales para cada invocación de función y ejecutar bucles costosos para establecer un par de valores predeterminados es una gran cantidad de sobrecarga.
mcurland
7

Si bien @roufamatic mostró el uso de la palabra clave de argumentos y @Ken mostró un gran ejemplo de un objeto para su uso, no siento que haya abordado realmente lo que está sucediendo en este caso y puede confundir a los futuros lectores o inculcar una mala práctica al no indicar explícitamente una función / método está destinado a tomar una cantidad variable de argumentos / parámetros.

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

Cuando otro desarrollador está revisando su código, es muy fácil asumir que esta función no toma parámetros. Especialmente si ese desarrollador no está al tanto de los argumentos palabra clave de . Debido a esto, es una buena idea seguir una pauta de estilo y ser coherente. Usaré Google para todos los ejemplos.

Digamos explícitamente que la misma función tiene parámetros variables:

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

Parámetro de objeto VS var_args

Puede haber ocasiones en que se necesite un objeto, ya que es el único método de mejores prácticas aprobado y considerado de un mapa de datos. Las matrices asociativas están mal vistas y desaconsejadas.

NOTA AL MARGEN: La palabra clave argumentos en realidad devuelve un objeto usando números como clave. La herencia prototípica es también la familia de objetos. Vea el final de la respuesta para el uso apropiado de la matriz en JS

En este caso, también podemos declarar esto explícitamente. Nota: Google no proporciona esta convención de nomenclatura, pero es un ejemplo de declaración explícita del tipo de un parámetro. Esto es importante si está buscando crear un patrón escrito más estricto en su código.

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

¿Cuál elegir?

Esto depende de las necesidades de su función y programa. Si, por ejemplo, simplemente está buscando devolver una base de valores en un proceso iterativo en todos los argumentos pasados, entonces seguramente se quedará con la palabra clave argumentos . Si necesita definir sus argumentos y mapear los datos, entonces el método del objeto es el camino a seguir. ¡Veamos dos ejemplos y luego terminamos!

Uso de argumentos

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

Uso de objetos

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

Acceder a una matriz en lugar de un objeto ("... args" El parámetro rest)

Como se menciona arriba de la respuesta, la palabra clave argumentos en realidad devuelve un objeto. Debido a esto, se deberá llamar a cualquier método que desee utilizar para una matriz. Un ejemplo de esto:

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

Para evitar esto, use el parámetro rest:

function varyArgArr (...var_args) {
    return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5
Brian Ellis
fuente
5

Use el argumentsobjeto dentro de la función para tener acceso a todos los argumentos pasados.

nickytonline
fuente
3

Tenga en cuenta que pasar un Objeto con propiedades con nombre como Ken sugirió agrega el costo de asignar y liberar el objeto temporal a cada llamada. Pasar argumentos normales por valor o referencia generalmente será el más eficiente. Para muchas aplicaciones, aunque el rendimiento no es crítico, pero para algunas sí puede serlo.

phbcanada
fuente