Número variável de JavaScript de argumentos para funcionar

531

Existe uma maneira de permitir vars "ilimitados" para uma função em JavaScript?

Exemplo:

load(var1, var2, var3, var4, var5, etc...)
load(var1)
Timo
fonte
duplicata relacionada / possível de stackoverflow.com/questions/4633125/…
Lucas
1
@ Lucas não, não é. Essa pergunta pergunta como chamar uma função com um número arbitrário de argumentos com os argumentos em uma matriz. Isso pergunta como lidar com essa chamada.
Scruffy
1
Para uma pesquisa mais fácil, essa função é chamada de 'função variável'.
gschenk 22/01

Respostas:

814

Claro, basta usar o argumentsobjeto.

function foo() {
  for (var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }
}
roufamatic
fonte
1
Tnx. É ótimo para analisar Strings do código nativo do Android para javascript em uma visualização na Web.
Johan Hoeksma
4
Esta solução funcionou melhor para mim. Obrigado. Mais informações sobre a palavra-chave argumentos AQUI .
usar o seguinte comando
26
argumentsé um objeto especial "Array-like", que significa que possui um comprimento, mas nenhuma outra função de matriz. Consulte developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/… para obter mais informações e esta resposta: stackoverflow.com/a/13145228/1766230
Lucas
4
Curiosamente, os métodos Array em Javascript foram definidos de forma que funcionem em qualquer objeto que possua uma lengthpropriedade. Isso inclui o argumentsobjeto. Sabendo disso, e que o método concatretorna uma cópia do 'array' é chamado, podemos facilmente converter o argumentsobjeto para uma matriz real, como este: var args = [].concat.call(arguments). Algumas pessoas preferem usar Array.prototype.concat.call, mas eu gosto do [], são curtas e doces!
Stijn de Witt
2
@YasirJan use[...arguments].join()
Willian D. Andrade
179

Nos navegadores (mais) recentes, você pode aceitar um número variável de argumentos com esta sintaxe:

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

Aqui está um pequeno exemplo:

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

Documentação e mais exemplos aqui: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters

Ramast
fonte
27
FYI é chamado de "o resto parâmetro sintaxe": developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
tanguy_k
4
+1 Esta é uma solução elegante e limpa. Especialmente adequado para passar uma longa lista de parâmetros para outra chamada de função e com possível que esses parâmetros variáveis ​​estejam em posição arbitrária.
haxpor
3
Lembre-se de que, de acordo com developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…, ele não é suportado no IE.
Roee Gavirel
2
Aqui está um suporte ao navegador tabela mostrando - caniuse.com/#feat=rest-parameters
Brian Burns
1
Que ótima resposta!
Ashish
113

Outra opção é passar seus argumentos em um objeto de contexto.

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

e use assim

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

Isso tem a vantagem de você poder adicionar quantos argumentos nomeados desejar e a função pode usá-los (ou não) como achar melhor.

Ken
fonte
4
Isso seria melhor, pois remove o acoplamento à ordem dos argumentos. Interfaces fracamente acopladas são uma boa prática padrão ...
Jonas Schubert Erlandsson
9
Claro, isso é melhor em alguns casos. Mas digamos que os argumentos individuais não estejam realmente relacionados um com o outro ou que todos tenham um significado igual (como elementos de matriz). Então o caminho da OP é o melhor.
rvighne
1
Isso também é bom porque, se você quiser, pode construir o contextargumento com o código e passá-lo antes que ele seja usado.
Nate CK
46

Concordo com a resposta de Ken como a mais dinâmica e gosto de dar um passo adiante. Se é uma função que você chama várias vezes com argumentos diferentes - eu uso o design de Ken, mas adiciono valores padrão:

function load(context) {

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

    var context = extend(defaults, context);

    // do stuff
}

Dessa forma, se você tiver muitos parâmetros, mas não necessariamente precisar defini-los com cada chamada para a função, poderá simplesmente especificar os não padrões. Para o método de extensão, você pode usar o método de extensão do jQuery ( $.extend()), criar seu próprio ou usar o seguinte:

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

Isso mesclará o objeto de contexto com os padrões e preencherá os valores indefinidos em seu objeto com os padrões.

mbeasley
fonte
3
+1. Bom truque. Economiza muita placa da caldeira para ter todos os parâmetros definidos, padrão ou não.
Neil
1
O _.defaults() método de sublinhado é uma alternativa muito boa para mesclar argumentos especificados e padrão.
mbeasley
18

Sim, assim:

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

load(1,2);
Canavar
fonte
18

É preferível usar a sintaxe do parâmetro rest como Ramast apontou.

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

Eu só quero adicionar uma propriedade legal do argumento ... args

  1. É uma matriz, e não um objeto como argumentos. Isso permite que você aplique funções como mapear ou classificar diretamente.
  2. Ele não inclui todos os parâmetros, mas apenas o que foi transmitido a partir dele. Por exemplo, a função (a, b, ... args) nesse caso, args contém o argumento 3 a argumentos.length
Nicola Pedretti
fonte
15

Como já mencionado, você pode usar o argumentsobjeto para recuperar um número variável de parâmetros de função.

Se você quiser chamar outra função com os mesmos argumentos, use apply. Você pode até adicionar ou remover argumentos convertendo argumentsem uma matriz. Por exemplo, esta função insere algum texto antes de efetuar o logon no console:

log() {
    let args = Array.prototype.slice.call(arguments);
    args = ['MyObjectName', this.id_].concat(args);
    console.log.apply(console, args);
}
Peter Tseng
fonte
solução agradável para converter argumentos em array. Foi útil para mim hoje.
Dmytro Medvid
8

Embora eu concorde geralmente que a abordagem de argumentos nomeados é útil e flexível (a menos que você se importe com a ordem, caso em que os argumentos são mais fáceis), tenho preocupações sobre o custo da abordagem de mbeasley (usando padrões e extensões). Essa é uma quantia extrema de custo para extrair valores padrão. Primeiro, os padrões são definidos dentro da função e, portanto, são preenchidos novamente em todas as chamadas. Segundo, você pode ler facilmente os valores nomeados e definir os padrões ao mesmo tempo usando ||. Não há necessidade de criar e mesclar outro novo objeto para obter essas informações.

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

   // do stuff
}

Essa é aproximadamente a mesma quantidade de código (talvez um pouco mais), mas deve ser uma fração do custo de tempo de execução.

Mccurland
fonte
Concordou, embora o dano dependa do tipo de valor ou do próprio padrão. Caso contrário, (parameter1=context.parameter1)===undefined && (parameter1=defaultValue1)ou para menos volume de código, uma pequena função auxiliar como: function def(providedValue, default) {return providedValue !== undefined ? providedValue : defaultValue;} var parameter1 = def(context.parameter1, defaultValue1)forneça padrões alternativos. No entanto, meu argumento ainda permanece: criar objetos extras para cada chamada de função e executar loops caros para definir alguns valores padrão são uma quantidade absurda de sobrecarga.
Mcurland
7

Embora o @roufamatic tenha mostrado o uso da palavra-chave argumentos e o @Ken tenha mostrado um ótimo exemplo de um objeto para uso, não sinto realmente o que está acontecendo neste caso e posso confundir futuros leitores ou incutir uma má prática por não declarar explicitamente uma função / method destina-se a receber uma quantidade variável de argumentos / parâmetros.

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

Quando outro desenvolvedor está pesquisando seu código, é muito fácil assumir que essa função não aceita parâmetros. Especialmente se esse desenvolvedor não estiver a par da palavra-chave argumentos . Por isso, é uma boa idéia seguir uma orientação de estilo e ser consistente. Vou usar o Google para todos os exemplos.

Vamos declarar explicitamente que a mesma função possui parâmetros variáveis:

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

Parâmetro de objeto VS var_args

Pode haver momentos em que um objeto é necessário, pois é o único método de boas práticas aprovado e considerado de um mapa de dados. Matrizes associativas são desaprovadas e desencorajadas.

SIDENOTE: A palavra-chave argumentos na verdade retorna um objeto usando números como a chave. A herança prototípica também é a família de objetos. Consulte o final da resposta para obter o uso apropriado da matriz em JS

Nesse caso, também podemos declarar explicitamente isso. Nota: esta convenção de nomenclatura não é fornecida pelo Google, mas é um exemplo de declaração explícita do tipo de um parâmetro. Isso é importante se você deseja criar um padrão digitado mais estrito no seu código.

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

Qual escolher?

Isso depende das necessidades de sua função e programa. Se, por exemplo, você está simplesmente procurando retornar uma base de valores em um processo iterativo em todos os argumentos passados, certamente se aterá à palavra-chave argumentos . Se você precisar definir seus argumentos e mapear os dados, o método do objeto é o caminho a seguir. Vejamos dois exemplos e então 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 objeto

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

Acessando uma matriz em vez de um objeto ("... args" O parâmetro restante)

Como mencionado acima da resposta, a palavra-chave argumentos realmente retorna um objeto. Por esse motivo, qualquer método que você deseja usar para uma matriz precisará ser chamado. Um exemplo disso:

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

Para evitar isso, use o parâmetro rest:

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

Use o argumentsobjeto quando estiver dentro da função para ter acesso a todos os argumentos passados.

nickytonline
fonte
3

Esteja ciente de que a passagem de um objeto com propriedades nomeadas, conforme sugerido por Ken, adiciona o custo de alocar e liberar o objeto temporário para cada chamada. Passar argumentos normais por valor ou referência geralmente será o mais eficiente. Para muitas aplicações, o desempenho não é crítico, mas para algumas pode ser.

phbcanada
fonte