Muitas funções integradas do JavaScript suportam um número arbitrário de argumentos.
Por exemplo:
Math.max(arg1, arg2, ..., argN)
– retorna o maior dos argumentos.
Object.assign(dest, src1, ..., srcN)
– copia propriedades de src1..N
para dest
.
…e assim por diante.
Neste capítulo aprenderemos como fazer o mesmo. E também como passar arrays para funções como parâmetros.
...
Uma função pode ser chamada com qualquer número de argumentos, não importa como seja definida.
Como aqui:
função soma(a, b) { retornar a + b; } alerta(soma(1, 2, 3, 4, 5) );
Não haverá erro por causa de argumentos “excessivos”. Mas claro que no resultado serão contabilizados apenas os dois primeiros, então o resultado no código acima é 3
.
O resto dos parâmetros podem ser incluídos na definição da função usando três pontos ...
seguidos do nome do array que os conterá. Os pontos significam literalmente “reunir os parâmetros restantes em uma matriz”.
Por exemplo, para reunir todos os argumentos em um array args
:
function sumAll(...args) { // args é o nome do array seja soma = 0; for (seja arg de args) soma += arg; soma de retorno; } alerta(somaTodos(1) ); //1 alerta(somaTodos(1, 2) ); //3 alerta(somaTodos(1, 2, 3) ); //6
Podemos optar por obter os primeiros parâmetros como variáveis e reunir apenas o restante.
Aqui, os dois primeiros argumentos vão para variáveis e o restante vai para o array titles
:
function showNome(nome, sobrenome, ...títulos) { alert(primeiroNome + '' + últimoNome); // Júlio César // o resto vai para o array de títulos // ou seja, títulos = ["Cônsul", "Imperador"] alerta(títulos[0]); // Cônsul alerta(títulos[1]); //Imperador alerta(títulos.comprimento); //2 } showName("Júlio", "César", "Cônsul", "Imperador");
Os demais parâmetros devem estar no final
Os demais parâmetros reúnem todos os argumentos restantes, portanto o seguinte não faz sentido e causa um erro:
function f(arg1, ...rest, arg2) { // arg2 depois de ...rest ?! //erro }
O ...rest
deve ser sempre o último.
Há também um objeto especial semelhante a um array chamado arguments
que contém todos os argumentos por seu índice.
Por exemplo:
function mostrarNome() { alerta(argumentos.comprimento); alerta(argumentos[0]); alerta(argumentos[1]); // é iterável // for(deixe arg de argumentos) alert(arg); } // mostra: 2, Júlio, César showName("Júlio", "César"); // mostra: 1, Ilya, indefinido (sem segundo argumento) showName("Ilya");
Antigamente, os parâmetros restantes não existiam na linguagem e usar arguments
era a única maneira de obter todos os argumentos da função. E ainda funciona, podemos encontrá-lo no código antigo.
Mas a desvantagem é que, embora arguments
sejam semelhantes a um array e iteráveis, não são um array. Ele não suporta métodos array, então não podemos chamar arguments.map(...)
por exemplo.
Além disso, sempre contém todos os argumentos. Não podemos capturá-los parcialmente, como fizemos com os parâmetros restantes.
Portanto, quando precisamos desses recursos, os parâmetros de descanso são os preferidos.
Funções de seta não possuem "arguments"
Se acessarmos o objeto arguments
a partir de uma função de seta, ele os retirará da função “normal” externa.
Aqui está um exemplo:
função f() { deixe showArg = () => alerta(argumentos[0]); mostrarArg(); } f(1); //1
Como lembramos, as funções de seta não possuem this
. Agora sabemos que eles também não possuem o objeto arguments
especiais.
Acabamos de ver como obter um array da lista de parâmetros.
Mas às vezes precisamos fazer exatamente o inverso.
Por exemplo, existe uma função interna Math.max que retorna o maior número de uma lista:
alerta(Math.max(3, 5, 1) ); //5
Agora digamos que temos um array [3, 5, 1]
. Como chamamos Math.max
com isso?
Passá-lo “como está” não funcionará, porque Math.max
espera uma lista de argumentos numéricos, não um único array:
deixe arr = [3, 5, 1]; alerta(Math.max(arr)); //NaN
E certamente não podemos listar manualmente os itens no código Math.max(arr[0], arr[1], arr[2])
, porque podemos não ter certeza de quantos existem. À medida que nosso script é executado, pode haver muitos ou nenhum. E isso ficaria feio.
Espalhe a sintaxe para o resgate! Parece semelhante aos parâmetros rest, também usando ...
, mas faz exatamente o oposto.
Quando ...arr
é usado na chamada de função, ele “expande” um objeto iterável arr
na lista de argumentos.
Para Math.max
:
deixe arr = [3, 5, 1]; alerta(Math.max(...arr)); // 5 (spread transforma array em uma lista de argumentos)
Também podemos passar vários iteráveis desta forma:
seja arr1 = [1, -2, 3, 4]; seja arr2 = [8, 3, -8, 1]; alerta(Math.max(...arr1, ...arr2) ); // 8
Podemos até combinar a sintaxe de spread com valores normais:
seja arr1 = [1, -2, 3, 4]; seja arr2 = [8, 3, -8, 1]; alerta(Math.max(1, ...arr1, 2, ...arr2, 25) ); //25
Além disso, a sintaxe spread pode ser usada para mesclar arrays:
deixe arr = [3, 5, 1]; seja arr2 = [8, 9, 15]; deixe mesclado = [0, ...arr, 2, ...arr2]; alerta(mesclado); // 0,3,5,1,2,8,9,15 (0, depois arr, depois 2, depois arr2)
Nos exemplos acima, usamos um array para demonstrar a sintaxe de propagação, mas qualquer iterável servirá.
Por exemplo, aqui usamos a sintaxe spread para transformar a string em um array de caracteres:
deixe str = "Olá"; alerta([...str]); // Olá
A sintaxe spread usa iteradores internamente para reunir elementos, da mesma forma que for..of
faz.
Então, para uma string, for..of
retorna caracteres e ...str
se torna "H","e","l","l","o"
. A lista de caracteres é passada para o inicializador do array [...str]
.
Para esta tarefa específica também poderíamos usar Array.from
, porque ele converte um iterável (como uma string) em um array:
deixe str = "Olá"; //Array.from converte um iterável em um array alerta(Array.from(str)); // Olá
O resultado é o mesmo que [...str]
.
Mas há uma diferença sutil entre Array.from(obj)
e [...obj]
:
Array.from
opera em arrays e iteráveis.
A sintaxe de propagação funciona apenas com iteráveis.
Então, para a tarefa de transformar algo em um array, Array.from
tende a ser mais universal.
Lembra quando falamos sobre Object.assign()
no passado?
É possível fazer a mesma coisa com a sintaxe de propagação.
deixe arr = [1, 2, 3]; deixe arrCopy = [...arr]; // espalha o array em uma lista de parâmetros // depois coloca o resultado em um novo array // os arrays possuem o mesmo conteúdo? alerta(JSON.stringify(arr) === JSON.stringify(arrCopy)); // verdadeiro // os arrays são iguais? alerta(arr === arrCopiar); // false (não é a mesma referência) // modificar nosso array inicial não modifica a cópia: arr.push(4); alerta(arr); //1, 2, 3, 4 alerta(arrCopy); //1, 2, 3
Observe que é possível fazer a mesma coisa para fazer uma cópia de um objeto:
deixe obj = {a: 1, b: 2, c: 3}; deixe objCopy = { ...obj }; // espalha o objeto em uma lista de parâmetros // então retorna o resultado em um novo objeto // os objetos possuem o mesmo conteúdo? alerta(JSON.stringify(obj) === JSON.stringify(objCopy)); // verdadeiro // os objetos são iguais? alert(obj === objCopiar); // false (não é a mesma referência) // modificar nosso objeto inicial não modifica a cópia: obj.d = 4; alerta(JSON.stringify(obj)); // {"a":1,"b":2,"c":3,"d":4} alerta(JSON.stringify(objCopy)); // {"a":1,"b":2,"c":3}
Esta forma de copiar um objeto é muito mais curta do que let objCopy = Object.assign({}, obj)
ou para um array let arrCopy = Object.assign([], arr)
então preferimos usá-lo sempre que pudermos.
Quando vemos "..."
no código, são os parâmetros restantes ou a sintaxe de propagação.
Existe uma maneira fácil de distingui-los:
Quando ...
está no final dos parâmetros da função, são “parâmetros restantes” e reúnem o restante da lista de argumentos em um array.
Quando ...
ocorre em uma chamada de função ou algo semelhante, é chamado de “sintaxe de propagação” e expande um array em uma lista.
Use padrões:
Parâmetros restantes são usados para criar funções que aceitam qualquer número de argumentos.
A sintaxe spread é usada para passar um array para funções que normalmente requerem uma lista de muitos argumentos.
Juntos, eles ajudam a viajar entre uma lista e uma série de parâmetros com facilidade.
Todos os argumentos de uma chamada de função também estão disponíveis em arguments
do “estilo antigo”: objeto iterável semelhante a um array.