Existe uma maneira de permitir vars "ilimitados" para uma função em JavaScript?
Exemplo:
load(var1, var2, var3, var4, var5, etc...)
load(var1)
Existe uma maneira de permitir vars "ilimitados" para uma função em JavaScript?
Exemplo:
load(var1, var2, var3, var4, var5, etc...)
load(var1)
Respostas:
Claro, basta usar o arguments
objeto.
function foo() {
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
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
length
propriedade. Isso inclui o arguments
objeto. Sabendo disso, e que o método concat
retorna uma cópia do 'array' é chamado, podemos facilmente converter o arguments
objeto 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!
[...arguments].join()
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
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.
context
argumento com o código e passá-lo antes que ele seja usado.
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.
_.defaults()
método de sublinhado é uma alternativa muito boa para mesclar argumentos especificados e padrão.
É 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
- É uma matriz, e não um objeto como argumentos. Isso permite que você aplique funções como mapear ou classificar diretamente.
- 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
Como já mencionado, você pode usar o arguments
objeto 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 arguments
em 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);
}
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.
(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.
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];
}
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});
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!
function sumOfAll (var_args) {
return arguments.reduce(function(a, b) {
return a + b;
}, 0);
}
sumOfAll(1,2,3); // returns 6
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
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
Use o arguments
objeto quando estiver dentro da função para ter acesso a todos os argumentos passados.
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.