É possível enviar um número variável de argumentos para uma função JavaScript?

171

É possível enviar um número variável de argumentos para uma função JavaScript, a partir de uma 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'

Recentemente, escrevi muito Python e é um padrão maravilhoso poder aceitar varargs e enviá-los. por exemplo

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'

É possível no JavaScript enviar uma matriz para ser tratada como a matriz de argumentos?

Corvo de fogo
fonte
4
Note-se que não é uma boa idéia usar um for - inlaço com o argumentsobjeto - um 'normal' loop iteração sobre a lengthpropriedade deve ser usada em vez
Yi Jiang
2
nunca foi um problema, você pode elaborar o porquê desse caso? O objeto argumentos é quase sempre pequeno o suficiente para ter uma melhoria insignificante de desempenho ao usar a versão agruments [0..length-1].
Fogo Corvo
Possível duplicado de número variável JavaScript de argumentos para a função
Anderson Verde
1
Eu acho que os argumentos não são uma matriz real, mas um objeto especial; portanto, a sintaxe "in" pode não funcionar, dependendo da implementação do JS.
O'Rooney

Respostas:

209

Atualização : Desde o ES6, você pode simplesmente usar a sintaxe de propagação ao chamar a função:

func(...arr);

Como o ES6 também se você espera tratar seus argumentos como uma matriz, também pode usar a sintaxe de propagação na lista de parâmetros, por exemplo:

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

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

E você pode combiná-lo com parâmetros normais, por exemplo, se você deseja receber os dois primeiros argumentos separadamente e o restante como uma matriz:

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

E talvez seja útil para você, que você possa saber quantos argumentos uma função espera:

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

Mas de qualquer maneira você pode passar um número arbitrário de argumentos ...

A sintaxe de propagação é mais curta e "mais doce" do que applye se você não precisar definir o thisvalor na chamada de função, este é o caminho a percorrer.

Aqui está um exemplo de aplicação , que era a primeira maneira de fazer isso:

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);

Atualmente, eu recomendo o uso applyapenas se você precisar passar um número arbitrário de argumentos de uma matriz e definir o thisvalor. applytakes é o thisvalor dos primeiros argumentos, que serão usados ​​na invocação da função. Se usarmos nullem código não estrito, a thispalavra - chave se referirá ao objeto Global (janela) dentro func, em modo estrito, ao usar explicitamente 'use strict 'ou nos módulos ES, nullserá usado.

Observe também que o argumentsobjeto não é realmente uma matriz, você pode convertê-lo por:

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

E no ES6:

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

Mas você raramente usa o argumentsobjeto diretamente hoje em dia, graças à sintaxe de propagação.

CMS
fonte
1
Obrigado, aplicar faz o truque, ligue neste caso não funciona. isso foi escrito por engano?
Fire Crow
Como você faz isso se a função é realmente um membro de uma classe? Isto está certo? theObject.member.apply (theObject, argumentos);
Baxissimo
4
Array.prototype.slice.call(arguments);impede otimizações de JS do navegador como a V8. Detalhes aqui
Dheeraj Bhaskar
2
1. Aparentemente, argumentsestá planejado para ser preterido em favor do operador de spread . Não tenho uma fonte para isso, mas ouvi em algum lugar. 2. O MDN afirma que o uso sliceno argumentsobjeto impede otimizações em mecanismos como o V8. Eles sugerem o uso do "construtor Array desprezado" Array.from(arguments), ou [...arguments]. Gostaria de atualizar sua resposta para o ES2015?
Braden Best
NB: Fui em frente e escrevi minha própria resposta .
Braden Best
75

Você pode realmente passar quantos valores desejar para qualquer função javascript. Os parâmetros explicitamente nomeados obterão os primeiros valores, mas TODOS os parâmetros serão armazenados na matriz de argumentos.

Para passar a matriz de argumentos no formato "descompactado", você pode usar o apply, assim (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
fonte
23

Os operadores splat e spread fazem parte do ES6, a próxima versão planejada do Javascript. Até agora, apenas o Firefox os suporta. Este código funciona no 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');

Observe a sintaxe do awkard para a propagação. A sintaxe usual de sprintf('The %s %s fox jumps over the %s dog.', ...arr);se ainda não é suportado . Você pode encontrar uma tabela de compatibilidade ES6 aqui .

Observe também o uso de for...ofoutra adição ao ES6. Usar for...inpara matrizes é uma má ideia .

Tgr
fonte
Sintaxe normal para propagação agora é suportada no Firefox e Chrome
usuário
8

A applyfunção usa dois argumentos; o objeto thisserá vinculado e os argumentos representados com uma matriz.

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

Existem dois métodos a partir do ES2015.

O argumentsObjeto

Este objeto é incorporado às funções e refere-se, adequadamente, aos argumentos de uma função. Tecnicamente, não é um array, portanto, as operações típicas do array não funcionarão nele. O método sugerido é usar Array.fromou o operador spread para criar uma matriz a partir dele.

Já vi outras respostas mencionadas usando slice. Não faça isso. Impede otimizações (fonte: MDN ).

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

No entanto, eu argumentaria que isso argumentsé problemático porque oculta o que uma função aceita como entrada. Uma argumentsfunção normalmente é escrita assim:

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

Às vezes, o cabeçalho da função é escrito da seguinte maneira para documentar os argumentos do modo C:

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

Mas isso é raro.

Por que é problemático, considere C, por exemplo. C é retrocompatível com um dialeto antigo pré-ANSI do idioma conhecido como K&R C. O K&R C permite que os protótipos de função tenham uma lista de argumentos vazia.

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

O ANSI C fornece um recurso para varargs( ...) e voidpara especificar uma lista de argumentos vazia.

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

Muitas pessoas inadvertidamente declaram funções com uma unspecifiedlista de argumentos ( int myfunction();), quando esperam que a função receba zero argumentos. Tecnicamente, isso é um bug, porque a função será aceitar argumentos. Qualquer número deles.

Uma varargsfunção adequada em C assume a forma:

int myFunction(int nargs, ...);

E o JavaScript realmente tem algo parecido com isso.

Operador de propagação / parâmetros de repouso

Eu já mostrei o operador spread, na verdade.

...name

É bastante versátil e também pode ser usado na lista de argumentos de uma função ("rest parameters") para especificar varargs de uma maneira bem documentada:

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

Ou como uma expressão lambda:

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

Eu prefiro muito o operador spread. É limpo e auto-documentado.

Braden Best
fonte
2
obrigado pela resposta atualizado, este antecede pós ES 2015, então eu estou feliz que você preencheu opções mais recentes
Fogo Corvo
5

Chama-se splatoperador. Você pode fazer isso em 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
fonte
4

Com o ES6, você pode usar os parâmetros de descanso para varagrs. Isso pega a lista de argumentos e a converte em uma matriz.

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

Este é um programa de amostra para calcular a soma de números inteiros para argumentos variáveis ​​e matriz em números inteiros. Espero que isto ajude.

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);
à risca
fonte
1
Algumas notas: 1. vardeclara uma function scopevariável. O novo lete as constpalavras - chave (ES 2015) declaram block scopevariáveis. Antes do ES 2015, no entanto, as variáveis ​​ainda deveriam ter sido içadas para o topo da função como uma questão de prática. 2. seus intervalos de função quando dado um valor inteiro por causa da sua aceitação errónea de uma matriz como um argumento (A pergunta sobre varargs; não aceitar matrizes como argumentos): CalculateSum(6) -> 0. (continuação no próximo post)
Braden Best
1
3. Não use funções de depuração como alert. Na verdade, apenas não use alert/prompt/confirm, ponto final. Use em console.logvez disso ao depurar. 4. A função deve ser returnum valor, não um valor alert. 5. evite, a PascalCasemenos que se refira a um "tipo" de dados. Ou seja, uma aula. Use camelCaseou em under_scorevez disso. 4. Não gosto da maneira como você declara suas funções. var f = function(){ ... }implica que você deseja que ele mude em algum momento, o que provavelmente não é sua intenção. Use a function name(){ ... }sintaxe convencional .
Braden Best
Para demonstrar todos esses pontos críticos, aqui está uma versão aprimorada da função em sua resposta.
Braden Best
2
(function(window) {
  var proxied = window.alert;
  window.alert = function() {
    return proxied.apply(window, arguments);
  };
}(this));

alert(13, 37);
yckart
fonte
1

Para aqueles que foram redirecionados aqui de Passando número variável de argumentos de uma função para outra (que não deve ser marcada como uma duplicata desta pergunta):

Se você está tentando passar um número variável de argumentos de uma função para outra, desde o JavaScript 1.8.5, você pode simplesmente chamar apply()a segunda função e passar o argumentsparâmetro:

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

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

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

Nota: O primeiro argumento é o thisparâmetro a ser usado. A passagem nullchamará a função do contexto global, ou seja, como uma função global em vez do método de algum objeto.

De acordo com este documento , a especificação ECMAScript 5 redefiniu o apply()método para pegar qualquer "objeto genérico de matriz", em vez de estritamente uma matriz. Assim, você pode passar diretamente a argumentslista para a segunda função.

Testado no Chrome 28.0, Safari 6.0.5 e IE 10. Experimente com este JSFiddle .

andrewtc
fonte
0

Sim, você pode passar a variável no. de argumentos para uma função. Você pode usarapply para conseguir isso.

Por exemplo:

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

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

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

Deseja que sua função reaja a um argumento de matriz ou argumentos de variável? Nesse último caso, tente:

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

Mas se você deseja lidar com um argumento de matriz

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

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