Nesta aula, vamos explorar recursos avançados de parâmetros em funções JavaScript. Você já conhece os parâmetros tradicionais, mas o JavaScript oferece mecanismos poderosos para tornar suas funções mais adaptáveis e legíveis. Dominar esses conceitos é essencial para escrever código moderno e eficiente.

Veremos como definir valores padrão para evitar undefined, como capturar múltiplos argumentos com rest parameters e como desestruturar objetos e arrays diretamente na assinatura da função. Esses recursos são amplamente utilizados em bibliotecas e frameworks, e fazem parte do dia a dia de qualquer desenvolvedor JavaScript.

Valores padrão

Antes do ES6, para definir valores padrão para parâmetros, era necessário verificar se o argumento era undefined dentro do corpo da função. Com o ES6, podemos atribuir valores padrão diretamente na declaração dos parâmetros. Isso torna o código mais limpo e reduz a verbosidade.

O valor padrão é usado apenas quando o argumento é undefined. Outros valores falsy como null, 0 ou '' não disparam o padrão. Além disso, o valor padrão pode ser uma expressão, que é avaliada no momento da chamada da função.

function saudacao(nome = "Visitante") {
  return `Olá, ${nome}!`;
}

console.log(saudacao("João")); // Olá, João!
console.log(saudacao());       // Olá, Visitante!
console.log(saudacao(undefined)); // Olá, Visitante!
console.log(saudacao(null));   // Olá, null!
// Valor padrão com expressão
function calcularPreco(preco, imposto = preco * 0.1) {
  return preco + imposto;
}

console.log(calcularPreco(100));        // 110
console.log(calcularPreco(100, 20));    // 120

É possível usar valores padrão em parâmetros anteriores, mas eles devem vir antes dos parâmetros que dependem deles. Caso contrário, ocorrerá erro de referência.

Rest parameters

O rest parameter (...args) permite representar um número indefinido de argumentos como um array. Ele coleta todos os argumentos adicionais passados para a função, além dos parâmetros nomeados. O rest parameter deve ser o último parâmetro da função.

Diferente do objeto arguments (que não é um array), o rest parameter é um array de verdade, com métodos como map, filter e reduce. Isso facilita a manipulação dos argumentos.

function somar(...numeros) {
  return numeros.reduce((total, num) => total + num, 0);
}

console.log(somar(1, 2, 3));       // 6
console.log(somar(10, 20, 30, 40)); // 100
console.log(somar());              // 0
// Rest com parâmetros nomeados
function listarNomes(prefixo, ...nomes) {
  return nomes.map(nome => `${prefixo} ${nome}`);
}

console.log(listarNomes("Sr.", "João", "Maria"));
// ["Sr. João", "Sr. Maria"]

Rest parameters são úteis em funções que aceitam um número variável de argumentos, como funções de logging, funções de soma, ou quando você quer delegar argumentos para outra função.

Desestruturação nos parâmetros

A desestruturação permite extrair valores de objetos ou arrays diretamente nos parâmetros da função. Isso torna o código mais conciso e legível, especialmente quando você precisa de apenas algumas propriedades de um objeto ou elementos de um array.

Você pode usar desestruturação com objetos, arrays, e até aninhar. Também é possível combinar com valores padrão para cada propriedade.

// Desestruturação de objeto
function exibirUsuario({ nome, idade, email = "não informado" }) {
  console.log(`Nome: ${nome}, Idade: ${idade}, Email: ${email}`);
}

const usuario = { nome: "Ana", idade: 30, email: "ana@email.com" };
exibirUsuario(usuario);
// Nome: Ana, Idade: 30, Email: ana@email.com

exibirUsuario({ nome: "Pedro", idade: 25 });
// Nome: Pedro, Idade: 25, Email: não informado
// Desestruturação de array
function primeiroEultimo([primeiro, ...resto]) {
  const ultimo = resto.pop();
  return [primeiro, ultimo];
}

console.log(primeiroEultimo([1, 2, 3, 4])); // [1, 4]
console.log(primeiroEultimo([5]));          // [5, undefined]

A desestruturação nos parâmetros é muito utilizada em funções que recebem objetos de configuração ou opções, pois permite extrair apenas as propriedades necessárias e fornecer valores padrão.

Boas práticas

Ao usar valores padrão, prefira expressões simples e evite efeitos colaterais (como chamadas de API ou mutações). Lembre-se de que o valor padrão é reavaliado a cada chamada se for uma expressão. Para rest parameters, sempre coloque-os por último e evite usar arguments em funções com rest parameter (pois eles coexistem, mas é confuso). Na desestruturação, use nomes descritivos e forneça valores padrão para evitar undefined.

Referências

Exercícios

  1. Escreva uma função chamada cumprimentar que recebe um nome e um parâmetro opcional saudacao com valor padrão "Olá". A função deve retornar a string no formato "{saudacao}, {nome}!".

    ✓ Resposta:
    function cumprimentar(nome, saudacao = "Olá") {
      return `${saudacao}, ${nome}!`;
    }
    
    console.log(cumprimentar("Maria"));          // Olá, Maria!
    console.log(cumprimentar("João", "Bom dia")); // Bom dia, João!
  2. Crie uma função concatenarStrings que aceita um separador como primeiro argumento e um número variável de strings usando rest parameter. A função deve retornar a concatenação das strings separadas pelo separador.

    ✓ Resposta:
    function concatenarStrings(separador, ...strings) {
      return strings.join(separador);
    }
    
    console.log(concatenarStrings("-", "a", "b", "c")); // "a-b-c"
    console.log(concatenarStrings(", ", "maçã", "banana")); // "maçã, banana"
  3. Implemente uma função obterCoordenadas que recebe um objeto com propriedades x e y (e opcionalmente z com valor padrão 0) usando desestruturação. A função deve retornar uma string "x: {x}, y: {y}, z: {z}".

    ✓ Resposta:
    function obterCoordenadas({ x, y, z = 0 }) {
      return `x: ${x}, y: ${y}, z: ${z}`;
    }
    
    console.log(obterCoordenadas({ x: 10, y: 20 }));       // x: 10, y: 20, z: 0
    console.log(obterCoordenadas({ x: 1, y: 2, z: 3 }));   // x: 1, y: 2, z: 3
  4. Crie uma função media que calcula a média de um número variável de argumentos usando rest parameter. Se nenhum argumento for passado, retorne 0.

    ✓ Resposta:
    function media(...numeros) {
      if (numeros.length === 0) return 0;
      const soma = numeros.reduce((acc, n) => acc + n, 0);
      return soma / numeros.length;
    }
    
    console.log(media(10, 20, 30)); // 20
    console.log(media());           // 0
  5. Escreva uma função exibirInfo que recebe um objeto com propriedades nome e idade usando desestruturação, e também um rest parameter ...hobbies. A função deve retornar uma string no formato "{nome} tem {idade} anos e gosta de: {hobbies listados separados por vírgula}".

    ✓ Resposta:
    function exibirInfo({ nome, idade }, ...hobbies) {
      const hobbiesStr = hobbies.join(", ");
      return `${nome} tem ${idade} anos e gosta de: ${hobbiesStr}`;
    }
    
    console.log(exibirInfo({ nome: "Lucas", idade: 28 }, "ler", "correr", "programar"));
    // Lucas tem 28 anos e gosta de: ler, correr, programar