¿Es posible enviar un número variable de argumentos a una función de JavaScript?

171

¿Es posible enviar un número variable de argumentos a una función de JavaScript, desde una matriz?

var arr = ['a','b','c']

var func = function()
{
    // debug 
    alert(arguments.length);
    //
    for(arg in arguments)
        alert(arg);
}

func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(arr); // prints 1, then 'Array'

Recientemente he escrito mucho Python y es un patrón maravilloso poder aceptar varargs y enviarlos. p.ej

def func(*args):
   print len(args)
   for i in args:
       print i

func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(*arr) // prints 4 which is what I want, then 'a','b','c','d'

¿Es posible en JavaScript enviar una matriz para ser tratada como la matriz de argumentos?

Cuervo de fuego
fuente
44
Tenga en cuenta que no es una buena idea usar un for - inbucle con el argumentsobjeto: en su lugar length, debe usarse un 'normal' para iterar el bucle sobre la propiedad
Yi Jiang
2
nunca ha sido un problema, ¿puedes explicar por qué ese es el caso? El objeto de argumentos es casi siempre lo suficientemente pequeño como para tener una mejora de rendimiento insignificante para usar la versión de agrupamientos [0..length-1].
Fire Crow
Posible duplicado de JavaScript variable número de argumentos para funcionar
Anderson Green
1
Creo que los argumentos no son una matriz real, sino un objeto especial, por lo que la sintaxis "in" podría no funcionar, dependiendo de la implementación de JS.
O'Rooney

Respuestas:

209

Actualización : desde ES6, simplemente puede usar la sintaxis de propagación al llamar a la función:

func(...arr);

Dado que ES6 también si espera tratar sus argumentos como una matriz, también puede usar la sintaxis extendida en la lista de parámetros, por ejemplo:

function func(...args) {
  args.forEach(arg => console.log(arg))
}

const values = ['a', 'b', 'c']
func(...values)
func(1, 2, 3)

Y puede combinarlo con parámetros normales, por ejemplo, si desea recibir los dos primeros argumentos por separado y el resto como una matriz:

function func(first, second, ...theRest) {
  //...
}

Y quizás sea útil para usted, que puede saber cuántos argumentos espera una función:

var test = function (one, two, three) {}; 
test.length == 3;

Pero de todos modos puedes pasar una cantidad arbitraria de argumentos ...

La sintaxis de propagación es más corta y "más dulce" que, applyy si no necesita establecer el thisvalor en la llamada a la función, este es el camino a seguir.

Aquí hay un ejemplo de aplicación , que era la forma anterior de hacerlo:

var arr = ['a','b','c'];

function func() {
  console.log(this); // 'test'
  console.log(arguments.length); // 3

  for(var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }

};

func.apply('test', arr);

Hoy en día solo recomiendo usar applysolo si necesita pasar un número arbitrario de argumentos de una matriz y establecer el thisvalor. applytoma es el thisvalor como los primeros argumentos, que se utilizarán en la invocación de la función, si usamosnull en código no estricto, la thispalabra clave se referirá al objeto Global (ventana) dentro func, en modo estricto, cuando se usa explícitamente 'use estricto 'o en módulos ES, nullse utilizarán.

También tenga en cuenta que el arguments objeto no es realmente una matriz, puede convertirlo de la siguiente manera:

var argsArray = Array.prototype.slice.call(arguments);

Y en ES6:

const argsArray = [...arguments] // or Array.from(arguments)

Pero rara vez se usa el argumentsobjeto directamente hoy en día gracias a la sintaxis de propagación.

CMS
fuente
1
Gracias, aplicar es el truco, llamar en este caso no funciona. fue escrito por error?
Fire Crow
¿Cómo lo haces si la función es realmente un miembro de una clase? ¿Es esto correcto? theObject.member.apply (theObject, argumentos);
Baxissimo
44
Array.prototype.slice.call(arguments);evita las optimizaciones JS del navegador como V8. Detalles aquí
Dheeraj Bhaskar
2
1. Aparentemente, argumentsestá planeado ser desaprobado a favor del operador de propagación . No tengo una fuente para esto, pero lo escuché en alguna parte. 2. MDN afirma que el uso sliceen el argumentsobjeto evita optimizaciones en motores como V8. Sugieren usar el "despreciado constructor de matrices" Array.from(arguments), o [...arguments]. ¿Le gustaría actualizar su respuesta para ES2015?
Braden Best
NB: Seguí adelante y escribí mi propia respuesta .
Braden Best
75

En realidad, puede pasar tantos valores como desee a cualquier función de JavaScript. Los parámetros nombrados explícitamente obtendrán los primeros valores, pero TODOS los parámetros se almacenarán en la matriz de argumentos.

Para pasar la matriz de argumentos en forma "desempaquetada", puede usar apply, así (cf. Javascript funcional ):

var otherFunc = function() {
   alert(arguments.length); // Outputs: 10
}

var myFunc = function() {
  alert(arguments.length); // Outputs: 10
  otherFunc.apply(this, arguments);
}
myFunc(1,2,3,4,5,6,7,8,9,10);
danben
fuente
23

Los operadores splat y spread son parte de ES6, la próxima versión planificada de Javascript. Hasta ahora, solo Firefox los admite. Este código funciona en FF16 +:

var arr = ['quick', 'brown', 'lazy'];

var sprintf = function(str, ...args)
{
    for (arg of args) {
        str = str.replace(/%s/, arg);
    }
    return str;
}

sprintf.apply(null, ['The %s %s fox jumps over the %s dog.', ...arr]);
sprintf('The %s %s fox jumps over the %s dog.', 'slow', 'red', 'sleeping');

Tenga en cuenta la sintaxis awkard para propagación. La sintaxis habitual de sprintf('The %s %s fox jumps over the %s dog.', ...arr);está todavía no está soportado . Puede encontrar una tabla de compatibilidad ES6 aquí .

Tenga en cuenta también el uso de for...of, otra adición de ES6. Usar for...inpara matrices es una mala idea .

Tgr
fuente
La sintaxis normal para propagación ahora es compatible con Firefox y Chrome
usuario el
8

La applyfunción toma dos argumentos; el objeto thisse vinculará y los argumentos se representarán con una matriz.

some_func = function (a, b) { return b }
some_func.apply(obj, ["arguments", "are", "here"])
// "are"
August Lilleaas
fuente
8

Hay dos métodos a partir de ES2015.

El argumentsobjeto

Este objeto está integrado en funciones, y se refiere, apropiadamente, a los argumentos de una función. Sin embargo, técnicamente no es una matriz, por lo que las operaciones de matriz típicas no funcionarán en ella. El método sugerido es usar Array.fromel operador de propagación para crear una matriz a partir de él.

He visto otras respuestas mencionar el uso slice. No hagas eso. Impide optimizaciones (fuente: MDN ).

Array.from(arguments)
[...arguments]

Sin embargo, diría que argumentses problemático porque oculta lo que una función acepta como entrada. Una argumentsfunción típicamente se escribe así:

function mean(){
    let args = [...arguments];
    return args.reduce((a,b)=>a+b) / args.length;
}

A veces, el encabezado de la función se escribe de la siguiente manera para documentar los argumentos en forma de C:

function mean(/* ... */){ ... }

Pero eso es raro.

En cuanto a por qué es problemático, tome C, por ejemplo. C es retrocompatible con un antiguo dialecto pre-ANSI del lenguaje conocido como K&R C. K&R C permite que los prototipos de funciones tengan una lista de argumentos vacía.

int myFunction();
/* This function accepts unspecified parameters */

ANSI C proporciona una función para varargs( ...) y voidpara especificar una lista de argumentos vacía.

int myFunction(void);
/* This function accepts no parameters */

Muchas personas declaran inadvertidamente funciones con una unspecifiedlista de argumentos ( int myfunction();), cuando esperan que la función tome cero argumentos. Esto es técnicamente un error debido a que la función de la voluntad aceptar los argumentos. Cualquier número de ellos.

Una varargsfunción adecuada en C toma la forma:

int myFunction(int nargs, ...);

Y JavaScript en realidad tiene algo similar a esto.

Operador extendido / Parámetros de descanso

Ya te he mostrado el operador de propagación, en realidad.

...name

Es bastante versátil y también se puede usar en la lista de argumentos de una función ("parámetros de reposo") para especificar varargs de una manera bien documentada:

function mean(...args){
    return args.reduce((a,b)=>a+b) / args.length;
}

O como una expresión lambda:

((...args)=>args.reduce((a,b)=>a+b) / args.length)(1,2,3,4,5); // 3

Prefiero mucho el operador de propagación. Es limpio y autodocumentado.

Braden Best
fuente
2
gracias por la respuesta actualizada, esta publicación es anterior a ES 2015, así que me alegra que hayas completado las opciones más recientes
Fire Crow
5

Se llama el splatoperador. Puedes hacerlo en JavaScript usando apply:

var arr = ['a','b','c','d'];
var func = function() {
    // debug 
    console.log(arguments.length);
    console.log(arguments);
}
func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(arr); // prints 1, then 'Array'
func.apply(null, arr); 
Chandra Patni
fuente
4

Con ES6 puede usar parámetros de reposo para varagrs. Esto toma la lista de argumentos y la convierte en una matriz.

function logArgs(...args) {
    console.log(args.length)
    for(let arg of args) {
        console.log(arg)
    }
}
theres.yer.problem
fuente
2

Este es un programa de muestra para calcular la suma de enteros para argumentos variables y matriz en enteros. Espero que esto ayude.

var CalculateSum = function(){
    calculateSumService.apply( null, arguments );
}

var calculateSumService = function(){
    var sum = 0;

    if( arguments.length === 1){
        var args = arguments[0];

        for(var i = 0;i<args.length; i++){
           sum += args[i]; 
        }
    }else{
        for(var i = 0;i<arguments.length; i++){
           sum += arguments[i]; 
        }        
    }

     alert(sum);

}


//Sample method call

// CalculateSum(10,20,30);         

// CalculateSum([10,20,30,40,50]);

// CalculateSum(10,20);
racha
fuente
1
Algunas notas: 1. vardeclara una function scopevariable. El nuevo lety las constpalabras clave (ES 2015) declaran block scopevariables. Sin embargo, antes de ES 2015, las variables deberían haberse elevado a la parte superior de la función como una práctica. 2. sus funciones se rompe cuando se administran 1 valor entero debido a la aceptación errónea de una matriz como un argumento (la pregunta se refiere a varargs; no aceptar matrices como argumentos): CalculateSum(6) -> 0. (continúa en el próximo post)
Braden Best
1
3. No utilice funciones de depuración como alert. En realidad, no lo uses alert/prompt/confirm, punto. Use en su console.loglugar al depurar. 4. La función debe ser returnun valor, no un valor alert. 5. evitar a PascalCasemenos que se refiera a un "tipo" de datos. Es decir, una clase. Uso camelCaseo en su under_scorelugar. 4. No me gusta la forma en que declaras tus funciones. var f = function(){ ... }implica que quieres que cambie en algún momento, lo que probablemente no sea tu intención. Utilice la function name(){ ... }sintaxis convencional en su lugar.
Braden Best
Para demostrar todos estos puntos críticos, aquí hay una versión mejorada de la función en su respuesta.
Braden Best
2
(function(window) {
  var proxied = window.alert;
  window.alert = function() {
    return proxied.apply(window, arguments);
  };
}(this));

alert(13, 37);
yckart
fuente
1

Para aquellos que fueron redirigidos aquí al pasar el número variable de argumentos de una función a otra (que no debe marcarse como un duplicado de esta pregunta):

Si está tratando de pasar un número variable de argumentos de una función a otra, desde JavaScript 1.8.5 simplemente puede llamar apply()a la segunda función y pasar el argumentsparámetro:

var caller = function()
{
    callee.apply( null, arguments );
}

var callee = function()
{
    alert( arguments.length );
}

caller( "Hello", "World!", 88 ); // Shows "3".

Nota: El primer argumento es el thisparámetro a usar. Pasar nullllamará a la función desde el contexto global, es decir, como una función global en lugar del método de algún objeto.

Según este documento , la especificación ECMAScript 5 redefinió el apply()método para tomar cualquier "objeto genérico similar a una matriz", en lugar de estrictamente una matriz. Por lo tanto, puede pasar directamente la argumentslista a la segunda función.

Probado en Chrome 28.0, Safari 6.0.5 e IE 10. Pruébelo con este JSFiddle .

andrewtc
fuente
0

Sí, puede pasar la variable no. de argumentos a una función. Puedes usar applypara lograr esto.

P.ej:

var arr = ["Hi","How","are","you"];

function applyTest(){
    var arg = arguments.length;
    console.log(arg);
}

applyTest.apply(null,arr);
Abhijit
fuente
0

¿Desea que su función reaccione a un argumento de matriz o argumentos variables? Si es lo último, intente:

var func = function(...rest) {
  alert(rest.length);

  // In JS, don't use for..in with arrays
  // use for..of that consumes array's pre-defined iterator
  // or a more functional approach
  rest.forEach((v) => console.log(v));
};

Pero si desea manejar un argumento de matriz

var fn = function(arr) {
  alert(arr.length);

  for(var i of arr) {
    console.log(i);
  }
};
Dev Yego
fuente