Como usar argumentos e parâmetros ES6

Publicados: 2022-03-10
Resumo rápido ↬ Os desenvolvedores estão usando cada vez mais os recursos do ECMAScript 6, e em breve esses recursos serão inevitáveis. Neste tutorial, você aprenderá como o ECMAScript 6 atualizou a manipulação de parâmetros em JavaScript e muito mais.

ECMAScript 6 (ou ECMAScript 2015) é a versão mais recente do padrão ECMAScript e melhorou notavelmente a manipulação de parâmetros em JavaScript. Agora podemos usar parâmetros de descanso, valores padrão e desestruturação, entre outras novidades.

Neste tutorial, exploraremos argumentos e parâmetros em detalhes e veremos como o ECMAScript 6 os atualizou.

Argumentos versus Parâmetros

Argumentos e parâmetros são muitas vezes referidos de forma intercambiável. No entanto, para os propósitos deste tutorial, faremos uma distinção. Na maioria dos padrões, os parâmetros (ou parâmetros formais) são o que é fornecido na declaração da função, e os argumentos (ou parâmetros reais) são o que é passado para a função. Considere esta função:

 function foo(param1, param2) { // do something } foo(10, 20);

Nesta função, param1 e param2 são parâmetros da função e os valores passados ​​para a função ( 10 e 20 ) são argumentos.

Operador de Spread (…)

No ECMAScript 5, o método apply() é uma ferramenta conveniente para passar um array como argumentos para uma função. Por exemplo, é comumente usado com o método Math.max() para encontrar o valor mais alto em uma matriz. Considere este fragmento de código:

 var myArray = [5, 10, 50]; Math.max(myArray); // Error: NaN Math.max.apply(Math, myArray); // 50

O método Math.max() não suporta arrays; ele aceita apenas números. Quando um array é passado para a função Math.max() , ele gera um erro. Mas quando o método apply() é usado, o array é enviado como números individuais, então o método Math.max() pode lidar com isso.

Mais depois do salto! Continue lendo abaixo ↓

Felizmente, com a introdução do operador spread no ECMAScript 6, não precisamos mais usar o método apply() . Com o operador spread, podemos expandir facilmente uma expressão em vários argumentos:

 var myArray = [5, 10, 50]; Math.max(...myArray); // 50

Aqui, o operador spread expande myArray para criar valores individuais para a função. Embora seja possível simular o operador spread usando apply() no ECMAScript 5, a sintaxe é confusa e carece da flexibilidade do operador spread. O operador de spread não é apenas mais fácil de usar, mas também possui mais recursos. Por exemplo, ele pode ser usado várias vezes e pode ser misturado com outros argumentos em uma chamada de function :

 function myFunction() { for(var i in arguments){ console.log(arguments[i]); } } var params = [10, 15]; myFunction(5, ...params, 20, ...[25]); // 5 10 15 20 25

Outra vantagem do operador spread é que ele pode ser facilmente usado com construtores:

 new Date(...[2016, 5, 6]); // Mon Jun 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)

Claro, poderíamos reescrever o código anterior em ECMAScript 5, mas precisaríamos usar um padrão complicado para evitar um erro de tipo:

 new Date.apply(null, [2016, 4, 24]); // TypeError: Date.apply is not a constructor new (Function.prototype.bind.apply(Date, [null].concat([2016, 5, 6]))); // Mon Jun 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)

Suporte ao navegador do operador de spread em chamadas de função

Navegadores de desktop:

cromada Raposa de fogo Internet Explorer Microsoft borda Ópera Safári
46 27 Compatível 7.1

Navegadores móveis:

Chrome para Android Firefox Móvel Safari móvel Ópera Móvel IE Móvel
46 27 8

Parâmetros de descanso

O parâmetro rest tem a mesma sintaxe do operador spread, mas em vez de expandir um array em parâmetros, ele coleta parâmetros e os transforma em um array.

 function myFunction(...options) { return options; } myFunction('a', 'b', 'c'); // ["a", "b", "c"]

Se não houver argumentos, o parâmetro rest será definido como um array vazio:

 function myFunction(...options) { return options; } myFunction(); // []

Um parâmetro rest é particularmente útil ao criar uma função variádica (uma função que aceita um número variável de argumentos). Tendo o benefício de serem arrays, os parâmetros rest podem substituir prontamente o objeto arguments (que explicaremos mais adiante neste tutorial). Considere esta função, escrita em ECMAScript 5:

 function checkSubstrings(string) { for (var i = 1; i < arguments.length; i++) { if (string.indexOf(arguments[i]) === -1) { return false; } } return true; } checkSubstrings('this is a string', 'is', 'this'); // true

Esta função verifica se uma string contém várias substrings. O primeiro problema com esta função é que temos que olhar dentro do corpo da function para ver que ela recebe vários argumentos. O segundo problema é que a iteração deve começar de 1 em vez de 0 , porque arguments[0] aponta para o primeiro argumento. Se posteriormente decidirmos adicionar outro parâmetro antes ou depois da string, podemos esquecer de atualizar o loop. Com os parâmetros de descanso, evitamos facilmente esses problemas:

 function checkSubstrings(string, ...keys) { for (var key of keys) { if (string.indexOf(key) === -1) { return false; } } return true; } checkSubstrings('this is a string', 'is', 'this'); // true

A saída desta função é a mesma da anterior. Aqui, novamente, a string do parâmetro é preenchida com o argumento que é passado primeiro, mas o restante dos argumentos é colocado em um array e atribuído às variáveis keys .

Usar o parâmetro rest em vez do objeto arguments melhora a legibilidade do código e evita problemas de otimização em JavaScript. No entanto, o parâmetro rest não é isento de limitações. Por exemplo, deve ser o último argumento; caso contrário, ocorrerá um erro de sintaxe:

 function logArguments(a, ...params, b) { console.log(a, params, b); } logArguments(5, 10, 15); // SyntaxError: parameter after rest parameter

Outra limitação é que apenas um parâmetro rest é permitido na declaração da function :

 function logArguments(...param1, ...param2) { } logArguments(5, 10, 15); // SyntaxError: parameter after rest parameter

Suporte ao navegador de parâmetros de descanso

Navegadores de desktop:

cromada Raposa de fogo Internet Explorer Microsoft borda Ópera Safári
47 15 Compatível 34

Navegadores móveis:

Chrome para Android Firefox Móvel Safari móvel Ópera Móvel IE Móvel
47 15

Parâmetros padrão

Parâmetros padrão no ECMAScript 5

JavaScript não suporta parâmetros padrão no ECMAScript 5, mas há uma solução fácil. Usando um operador OR lógico ( || ) dentro da função, podemos simular facilmente os parâmetros padrão no ECMAScript 5. Considere esta função:

 function foo(param1, param2) { param1 = param1 || 10; param2 = param2 || 10; console.log(param1, param2); } foo(5, 5); // 5 5 foo(5); // 5 10 foo(); // 10 10

Esta função espera dois argumentos, mas quando for chamada sem argumentos, usará os valores padrão. Dentro da função, os argumentos ausentes são definidos automaticamente como indefinidos; assim, podemos detectar esses argumentos e declarar valores padrão para eles. Para detectar argumentos ausentes e definir valores padrão, usamos o operador OR lógico ( || ). Este operador examina seu primeiro argumento: Se for verdadeiro, o operador o retorna; se não for, o operador retorna seu segundo argumento.

Essa abordagem é comumente usada em funções, mas tem uma falha. Passar 0 ou null também acionará um valor padrão, porque esses são considerados valores falsos. Então, se realmente precisarmos passar 0 ou null para esta função, precisaríamos de uma maneira alternativa de verificar se um argumento está faltando:

 function foo(param1, param2) { if(param1 === undefined){ param1 = 10; } if(param2 === undefined){ param2 = 10; } console.log(param1, param2); } foo(0, null); // 0, null foo(); // 10, 10

Dentro desta função, os tipos de argumentos passados ​​são verificados para garantir que sejam indefinidos antes que os valores padrão sejam atribuídos. Essa abordagem requer um pouco mais de código, mas é uma alternativa mais segura e nos permite passar 0 e null para a função.

Parâmetros padrão no ECMAScript 6

Com o ECMAScript 6, não precisamos mais verificar valores indefinidos para simular parâmetros padrão. Agora podemos colocar os valores padrão diretamente na declaração da function :

 function foo(a = 10, b = 10) { console.log(a, b); } foo(5); // 5 10 foo(0, null); // 0 null

Como você pode ver, omitir um argumento aciona o valor padrão, mas passar 0 ou null não. Podemos até usar funções para recuperar valores para parâmetros padrão:

 function getParam() { alert("getParam was called"); return 3; } function multiply(param1, param2 = getParam()) { return param1 * param2; } multiply(2, 5); // 10 multiply(2); // 6 (also displays an alert dialog)

Observe que a função getParam é chamada somente se o segundo argumento for omitido. Assim, quando chamamos a função multiply() com dois parâmetros, o alerta não será exibido.

Outra característica interessante dos parâmetros padrão é que podemos nos referir a outros parâmetros e variáveis ​​na declaração da function :

 function myFunction(a=10, b=a) { console.log('a = ' + a + '; b = ' + b); } myFunction(); // a=10; b=10 myFunction(22); // a=22; b=22 myFunction(2, 4); // a=2; b=4

Você pode até mesmo realizar operações na declaração da function :

 function myFunction(a, b = ++a, c = a*b) { console.log(c); } myFunction(5); // 36

Observe que, ao contrário de algumas outras linguagens, o JavaScript avalia os parâmetros padrão no momento da chamada:

 function add(value, array = []) { array.push(value); return array; } add(5); // [5] add(6); // [6], not [5, 6]

Suporte de navegador de parâmetro padrão

Navegadores de desktop:

Funcionalidade cromada Raposa de fogo Internet Explorer Microsoft borda Ópera Safári
Suporte básico 49 15 14
Parâmetros sem padrões após o parâmetro padrão 49 26 14

Navegadores móveis:

Funcionalidade Chrome para Android Firefox Móvel Safari móvel Ópera Móvel IE Móvel
Suporte básico 49 15
Parâmetros sem padrões após o parâmetro padrão 46 26

Desestruturando

A desestruturação é um novo recurso do ECMAScript 6 que nos permite extrair valores de arrays e objetos e atribuí-los a variáveis ​​usando uma sintaxe semelhante a literais de objetos e arrays. A sintaxe é clara e fácil de entender e é particularmente útil ao passar argumentos para uma função.

No ECMAScript 5, um objeto de configuração geralmente é usado para manipular um grande número de parâmetros opcionais, especialmente quando a ordem das propriedades não importa. Considere esta função:

 function initiateTransfer(options) { var protocol = options.protocol, port = options.port, delay = options.delay, retries = options.retries, timeout = options.timeout, log = options.log; // code to initiate transfer } options = { protocol: 'http', port: 800, delay: 150, retries: 10, timeout: 500, log: true }; initiateTransfer(options);

Esse padrão é comumente usado por desenvolvedores de JavaScript e funciona bem, mas temos que olhar dentro do corpo da function para ver quais parâmetros ele espera. Com parâmetros desestruturados, podemos indicar claramente os parâmetros na declaração da function :

 function initiateTransfer({protocol, port, delay, retries, timeout, log}) { // code to initiate transfer }; var options = { protocol: 'http', port: 800, delay: 150, retries: 10, timeout: 500, log: true } initiateTransfer(options);

Nesta função, usamos um padrão de desestruturação de objetos, em vez de um objeto de configuração. Isso torna nossa função não apenas mais concisa, mas mais fácil de ler.

Também podemos combinar parâmetros desestruturados com regulares:

 function initiateTransfer(param1, {protocol, port, delay, retries, timeout, log}) { // code to initiate transfer } initiateTransfer('some value', options);

Observe que um erro de tipo será lançado se os parâmetros forem omitidos na chamada da function :

 function initiateTransfer({protocol, port, delay, retries, timeout, log}) { // code to initiate transfer } initiateTransfer(); // TypeError: Cannot match against 'undefined' or 'null'

Esse é o comportamento desejado quando precisamos que os parâmetros sejam obrigatórios, mas e se quisermos que eles sejam opcionais? Para evitar esse erro quando os parâmetros estão ausentes, precisamos atribuir um valor padrão aos parâmetros desestruturados:

 function initiateTransfer({protocol, port, delay, retries, timeout, log} = {}) { // code to initiate transfer } initiateTransfer(); // no error

Nesta função, um objeto vazio é fornecido como valor padrão para os parâmetros desestruturados. Agora, se esta função for chamada sem nenhum parâmetro, nenhum erro ocorrerá.

Também podemos atribuir um valor padrão para cada parâmetro desestruturado:

 function initiateTransfer({ protocol = 'http', port = 800, delay = 150, retries = 10, timeout = 500, log = true }) { // code to initiate transfer }

Neste exemplo, cada propriedade tem um parâmetro padrão, eliminando a necessidade de verificarmos manualmente os parâmetros indefinidos e atribuir valores padrão dentro do corpo da function .

Desestruturando o suporte do navegador

Navegadores de desktop:

Funcionalidade cromada Raposa de fogo Internet Explorer Microsoft borda Ópera Safári
Suporte básico 49 2,0 14 7.1
Parâmetro desestruturado com atribuição de valor padrão 49 47 14

Navegadores móveis:

Funcionalidade Chrome para Android Firefox Móvel Safari móvel Ópera Móvel IE Móvel
Suporte básico 49 1 8
Parâmetros sem padrões após o parâmetro padrão 49 47

Argumentos de passagem

Existem duas maneiras de passar argumentos para uma função: por referência ou por valor. Modificar um argumento que é passado por referência é refletido globalmente, mas modificar um argumento que é passado por valor é refletido apenas dentro da função.

Em algumas linguagens, como Visual Basic e PowerShell, temos a opção de especificar se um argumento deve ser passado por referência ou por valor, mas esse não é o caso do JavaScript.

Passando argumentos por valor

Tecnicamente, JavaScript só pode passar por valor. Quando passamos um argumento para uma função por valor, uma cópia desse valor é criada dentro do escopo da function . Assim, quaisquer alterações no valor são refletidas apenas dentro da function . Considere este exemplo:

 var a = 5; function increment(a) { a = ++a; console.log(a); } increment(a); // 6 console.log(a); // 5

Aqui, modificar o argumento dentro da função não afeta o valor original. Assim, quando a variável é registrada de fora da função, o valor impresso ainda é 5 .

Passando argumentos por referência

Em JavaScript, tudo é passado por valor, mas quando passamos uma variável que se refere a um objeto (incluindo arrays), o “valor” é uma referência ao objeto, e alterar uma propriedade de um objeto referenciado por uma variável altera o objeto subjacente.

Considere esta função:

 function foo(param){ param.bar = 'new value'; } obj = { bar : 'value' } console.log(obj.bar); // value foo(obj); console.log(obj.bar); // new value

Como você pode ver, a propriedade do objeto é modificada dentro da função, mas o valor modificado é visível fora da função.

Quando passamos um valor não primitivo, como um array ou objeto, nos bastidores é criada uma variável que aponta para a localização do objeto original na memória. Essa variável é então passada para a função e modificá-la afetará o objeto original.

Verificação de tipo e parâmetros ausentes ou extras

Em uma linguagem fortemente tipada, temos que especificar o tipo de parâmetros na declaração da function , mas o JavaScript não possui esse recurso. Em JavaScript, não importa que tipo de dados ou quantos argumentos passamos para uma função.

Suponha que temos uma função que aceita apenas um argumento. Quando chamamos essa função, não estamos limitados a passar apenas um argumento para a função; somos livres para passar um, dois ou mais argumentos! Podemos até optar por não passar nada, e nenhum erro ocorrerá.

O número de argumentos e parâmetros pode diferir de duas maneiras:

  • Menos argumentos do que parâmetros .
    Os parâmetros ausentes serão iguais a undefined .
  • Mais argumentos do que parâmetros .
    Os parâmetros extras serão ignorados, mas podem ser recuperados por meio dos argumentos de variáveis ​​especiais do tipo array (discutidos a seguir).

Argumentos obrigatórios

Se um argumento estiver faltando em uma chamada de function , ele será definido como undefined . Podemos aproveitar esse comportamento e lançar um erro se um argumento for omitido:

 function foo(mandatory, optional) { if (mandatory === undefined) { throw new Error('Missing parameter: mandatory'); } }

No ECMAScript 6, podemos levar isso adiante e usar parâmetros padrão para definir argumentos obrigatórios:

 function throwError() { throw new Error('Missing parameter'); } function foo(param1 = throwError(), param2 = throwError()) { // do something } foo(10, 20); // ok foo(10); // Error: missing parameter

Objeto de argumentos

O suporte para parâmetros rest foi adicionado ao ECMAScript 4 com a intenção de substituir o objeto arguments , mas o ECMAScript 4 nunca se concretizou. Com o lançamento do ECMAScript 6, o JavaScript agora suporta oficialmente os parâmetros restantes. Ele também anulou o plano de descartar o suporte para o objeto de arguments .

O objeto arguments é um objeto do tipo array que está disponível em todas as funções. Ele permite que os valores do argument passados ​​para a função sejam recuperados por número, em vez de por nome. O objeto nos permite passar qualquer número de argumentos para uma função. Considere o seguinte fragmento de código:

 function checkParams(param1) { console.log(param1); // 2 console.log(arguments[0], arguments[1]); // 2 3 console.log(param1 + arguments[0]); // 2 + 2 } checkParams(2, 3);

Esta função espera receber apenas um argumento. Quando o chamamos com dois argumentos, o primeiro argumento é acessível na função pelo nome do parâmetro param1 ou pelos argumentos object arguments[0] , mas o segundo argumento é acessível apenas como arguments[1] . Além disso, observe que o objeto arguments pode ser usado em conjunto com argumentos nomeados.

O objeto arguments contém uma entrada para cada argumento passado para a função e o índice da primeira entrada começa em 0 . Se quiséssemos acessar mais argumentos no exemplo acima, escreveríamos arguments[2] , arguments[3] e assim por diante.

Poderíamos até mesmo pular a configuração de parâmetros nomeados e usar apenas o objeto arguments :

 function checkParams() { console.log(arguments[1], arguments[0], arguments[2]); } checkParams(2, 4, 6); // 4 2 6

Na verdade, os parâmetros nomeados são uma conveniência, não uma necessidade. Da mesma forma, os parâmetros rest podem ser usados ​​para refletir os argumentos passados:

 function checkParams(...params) { console.log(params[1], params[0], params[2]); // 4 2 6 console.log(arguments[1], arguments[0], arguments[2]); // 4 2 6 } checkParams(2, 4, 6);

O objeto arguments é um objeto do tipo array, mas não possui métodos de array como slice() e foreach() . Para usar métodos de array no objeto de arguments , o objeto primeiro precisa ser convertido em um array real:

 function sort() { var a = Array.prototype.slice.call(arguments); return a.sort(); } sort(40, 20, 50, 30); // [20, 30, 40, 50]

Nesta função, Array.prototype.slice.call() é usado como uma forma rápida de converter o objeto arguments em um array. Em seguida, o método sort() classifica os itens do array e o retorna.

ECMAScript 6 tem uma maneira ainda mais direta. Array.from() , uma nova adição no ECMAScript 6, cria um novo array a partir de qualquer objeto do tipo array:

 function sort() { var a = Array.from(arguments); return a.sort(); } sort(40, 20, 50, 30); // [20, 30, 40, 50]

A propriedade de comprimento

Embora o objeto arguments não seja tecnicamente um array, ele possui uma propriedade length que pode ser usada para verificar o número de argumentos passados ​​para uma função:

 function countArguments() { console.log(arguments.length); } countArguments(); // 0 countArguments(10, null, "string"); // 3

Usando a propriedade length , temos um melhor controle sobre o número de argumentos passados ​​para uma função. Por exemplo, se uma função requer dois argumentos para funcionar, podemos usar a propriedade length para verificar o número de argumentos passados ​​e gerar um erro se forem menos do que o esperado:

 function foo(param1, param2) { if (arguments.length < 2) { throw new Error("This function expects at least two arguments"); } else if (arguments.length === 2) { // do something } }

Parâmetros de descanso são arrays, então eles têm uma propriedade de length . No ECMAScript 6, o código anterior pode ser reescrito com os parâmetros rest:

 function foo(...params) { if (params.length < 2) { throw new Error("This function expects at least two arguments"); } else if (params.length === 2) { // do something } }

As propriedades do chamador e do chamador

A propriedade callee refere-se à função que está sendo executada no momento, e o caller refere-se à função que chamou a função atualmente em execução. No modo estrito do ECMAScript 5, essas propriedades são preteridas e tentar acessá-las causa um TypeError.

A propriedade arguments.callee é útil em funções recursivas (uma função recursiva é uma função regular que se refere a si mesma por seu nome), especialmente quando o nome da função não está disponível (uma função anônima). Como uma função anônima não tem nome, a única maneira de se referir a ela é por arguments.callee .

 var result = (function(n) { if (n <= 1) { return 1; } else { return n * arguments.callee(n - 1); } })(4); // 24

Objetos de argumentos nos modos estrito e não estrito

No modo não estrito do ECMAScript 5, o objeto arguments possui um recurso incomum: ele mantém seus valores sincronizados com os valores dos parâmetros nomeados correspondentes.

Considere o seguinte fragmento de código:

 function foo(param) { console.log(param === arguments[0]); // true arguments[0] = 500; console.log(param === arguments[0]); // true return param } foo(200); // 500

Dentro desta função, um novo valor é atribuído a arguments[0] . Como os valores de arguments sempre ficam sincronizados com os valores de parâmetros nomeados, a alteração para arguments[0] também alterará o valor de param . Na verdade, eles são como dois nomes diferentes para a mesma variável. No modo estrito do ECMAScript 5, esse comportamento confuso do objeto arguments foi removido:

 "use strict"; function foo(param) { console.log(param === arguments[0]); // true arguments[0] = 500; console.log(param === arguments[0]); // false return param } foo(200); // 200

Desta vez, alterar arguments[0] não afeta param e a saída é conforme o esperado. A saída dessa função no ECMAScript 6 é a mesma do modo estrito do ECMAScript 5, mas lembre-se de que quando os valores padrão são usados ​​na declaração da function , o objeto arguments não é afetado:

 function foo(param1, param2 = 10, param3 = 20) { console.log(param1 === arguments[0]); // true console.log(param2 === arguments[1]); // true console.log(param3 === arguments[2]); // false console.log(arguments[2]); // undefined console.log(param3); // 20 } foo('string1', 'string2');

Nesta função, embora param3 tenha um valor padrão, não é igual a arguments[2] porque apenas dois argumentos são passados ​​para a função. Em outras palavras, definir valores padrão não tem efeito no objeto de arguments .

Conclusão

O ECMAScript 6 trouxe centenas de pequenas e grandes melhorias ao JavaScript. Cada vez mais, os desenvolvedores estão usando os recursos do ECMAScript 6, e em breve esses recursos serão inevitáveis. Neste tutorial, aprendemos como o ECMAScript 6 atualizou a manipulação de parâmetros em JavaScript, mas apenas arranhamos a superfície do ECMAScript 6. Vale a pena conferir muitos outros recursos novos e interessantes da linguagem.

Links

  • Tabela de compatibilidade ECMAScript 6, Juriy Zaytsev
  • “Especificação de linguagem ECMAScript 2015,” ECMA International