Parâmetros padrão, rest e spread operator

1. Parâmetros Padrão (Default Parameters)

Os parâmetros padrão permitem definir valores iniciais para argumentos de funções quando nenhum valor ou undefined é passado. A sintaxe é direta:

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

console.log(saudacao("Maria")); // Olá, Maria!
console.log(saudacao());        // Olá, Visitante!

É importante entender que apenas undefined aciona o valor padrão. Outros valores falsy como null, 0 ou "" são mantidos:

function testar(valor = "padrão") {
  return valor;
}

console.log(testar(undefined)); // "padrão"
console.log(testar(null));      // null
console.log(testar(0));         // 0
console.log(testar(""));        // ""

Valores padrão podem ser expressões ou resultados de funções:

function gerarId(prefixo = "user", timestamp = Date.now()) {
  return `${prefixo}_${timestamp}`;
}

console.log(gerarId()); // user_1697123456789

2. Parâmetros Padrão no Contexto de Node.js e React

No React, parâmetros padrão evitam erros em handlers de eventos:

function Botao({ onClick = () => {}, texto = "Clique aqui" }) {
  return <button onClick={onClick}>{texto}</button>;
}

Em middlewares do Express (Node.js), valores padrão configuram opções:

function loggerMiddleware(opcoes = { nivel: "info", formato: "json" }) {
  return (req, res, next) => {
    console.log(`[${opcoes.nivel}] ${req.method} ${req.url}`);
    next();
  };
}

app.use(loggerMiddleware());
app.use(loggerMiddleware({ nivel: "debug" }));

Exemplo prático: componente React com props opcionais:

function Card({ titulo = "Sem título", conteudo = "", corFundo = "#fff" }) {
  return (
    <div style={{ backgroundColor: corFundo, padding: "20px" }}>
      <h2>{titulo}</h2>
      <p>{conteudo}</p>
    </div>
  );
}

// Uso
<Card titulo="Meu Card" conteudo="Conteúdo personalizado" />
<Card /> // Renderiza com valores padrão

3. Parâmetros Rest (...args)

O operador rest coleta argumentos restantes em um array real, diferentemente do objeto arguments que é array-like:

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

console.log(somarTodos(1, 2, 3, 4)); // 10

// Comparação com arguments
function usandoArguments() {
  return Array.from(arguments).reduce((acc, val) => acc + val, 0);
}

Rest em arrow functions e métodos de classe:

const multiplicar = (multiplicador, ...numeros) => {
  return numeros.map(n => n * multiplicador);
};

console.log(multiplicar(2, 1, 2, 3)); // [2, 4, 6]

class Calculadora {
  somar(...valores) {
    return valores.reduce((acc, v) => acc + v, 0);
  }
}

4. Parâmetros Rest em Node.js e React

Funções utilitárias com número variável de argumentos:

// utils/formatacao.js
function formatarMensagem(template, ...valores) {
  return valores.reduce((msg, val, i) => {
    return msg.replace(`{${i}}`, val);
  }, template);
}

console.log(formatarMensagem("Olá {0}, hoje é {1}", "João", "segunda-feira"));

Hooks personalizados com parâmetros dinâmicos:

function useFormulario(estadoInicial = {}, ...validadores) {
  const [dados, setDados] = React.useState(estadoInicial);
  const [erros, setErros] = React.useState({});

  const validar = () => {
    const novosErros = {};
    validadores.forEach(validador => {
      const resultado = validador(dados);
      if (resultado) Object.assign(novosErros, resultado);
    });
    setErros(novosErros);
    return Object.keys(novosErros).length === 0;
  };

  return { dados, erros, setDados, validar };
}

Função de logging flexível em servidor Node.js:

function logServidor(nivel, mensagem, ...metadados) {
  const entrada = {
    timestamp: new Date().toISOString(),
    nivel,
    mensagem,
    metadados: metadados.length > 0 ? metadados : undefined
  };

  console.log(JSON.stringify(entrada));
}

logServidor("INFO", "Servidor iniciado");
logServidor("ERRO", "Falha na conexão", { db: "mongo" }, { tentativa: 3 });

5. Spread Operator em Arrays e Objetos

Propagação de arrays para cópia, concatenação e inserção:

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

// Cópia
const copia = [...arr1];

// Concatenação
const combinado = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]

// Inserção
const comItem = [0, ...arr1, 4]; // [0, 1, 2, 3, 4]

Propagação de objetos para clonagem rasa e mesclagem:

const usuario = { nome: "Ana", idade: 30 };
const endereco = { cidade: "São Paulo", uf: "SP" };

// Clonagem rasa
const clone = { ...usuario };

// Mesclagem
const completo = { ...usuario, ...endereco };
// { nome: "Ana", idade: 30, cidade: "São Paulo", uf: "SP" }

// Ordem importa: propriedades posteriores sobrescrevem anteriores
const atualizado = { ...usuario, idade: 31 };

Uso com destructuring:

const pessoa = { nome: "Carlos", idade: 25, email: "carlos@email.com" };
const { nome, ...resto } = pessoa;

console.log(nome);  // "Carlos"
console.log(resto); // { idade: 25, email: "carlos@email.com" }

6. Spread Operator no Contexto de React

Atualização imutável de estado com hooks:

function ListaTarefas() {
  const [tarefas, setTarefas] = React.useState([]);
  const [novaTarefa, setNovaTarefa] = React.useState("");

  const adicionarTarefa = () => {
    setTarefas([...tarefas, { id: Date.now(), texto: novaTarefa, concluida: false }]);
    setNovaTarefa("");
  };

  const alternarTarefa = (id) => {
    setTarefas(tarefas.map(t => 
      t.id === id ? { ...t, concluida: !t.concluida } : t
    ));
  };

  return (
    <div>
      <input value={novaTarefa} onChange={e => setNovaTarefa(e.target.value)} />
      <button onClick={adicionarTarefa}>Adicionar</button>
      <ul>
        {tarefas.map(t => (
          <li key={t.id} onClick={() => alternarTarefa(t.id)}>
            {t.texto} - {t.concluida ? "✓" : "✗"}
          </li>
        ))}
      </ul>
    </div>
  );
}

Passagem de props dinâmicas para componentes filhos:

function Tabela({ dados, colunas, ...propsTabela }) {
  return (
    <table {...propsTabela}>
      <thead>
        <tr>
          {colunas.map(col => <th key={col}>{col}</th>)}
        </tr>
      </thead>
      <tbody>
        {dados.map((linha, i) => (
          <tr key={i}>
            {colunas.map(col => <td key={col}>{linha[col]}</td>)}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

// Uso
<Tabela 
  dados={usuarios} 
  colunas={["nome", "email"]} 
  className="tabela-azul" 
  id="tabela-usuarios" 
/>

7. Combinações Avançadas e Boas Práticas

Uso simultâneo dos três operadores:

function configurarServidor(
  porta = 3000, 
  host = "localhost", 
  ...middlewares
) {
  const config = {
    porta,
    host,
    middlewares: middlewares.length > 0 ? middlewares : [loggerMiddleware()]
  };

  return {
    ...config,
    url: `http://${host}:${porta}`,
    iniciar: () => console.log(`Servidor rodando em ${config.url}`)
  };
}

const servidor = configurarServidor(
  8080, 
  "0.0.0.0", 
  authMiddleware, 
  corsMiddleware
);

Imutabilidade e efeitos colaterais:

// Ruim: mutação direta
function atualizarUsuario(usuario, novosDados) {
  usuario.nome = novosDados.nome; // Mutação!
  return usuario;
}

// Bom: imutabilidade com spread
function atualizarUsuario(usuario, novosDados) {
  return { ...usuario, ...novosDados };
}

Armadilhas comuns: spread realiza cópia rasa (shallow copy)

const obj = { a: 1, b: { c: 2 } };
const copia = { ...obj };

copia.b.c = 99; // Modifica o original também!
console.log(obj.b.c); // 99

// Para deep copy, use structuredClone ou bibliotecas
const copiaProfunda = structuredClone(obj);

8. Exemplos Integrados JavaScript + Node.js + React

Função utilitária compartilhada entre frontend e backend:

// shared/utils.js
export function criarQueryString(base = {}, ...parametros) {
  const params = parametros.reduce((acc, p) => ({ ...acc, ...p }), {});
  const query = { ...base, ...params };

  return Object.entries(query)
    .filter(([_, v]) => v !== undefined && v !== null)
    .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
    .join("&");
}

Componente React consumindo API Node.js com parâmetros dinâmicos:

function useApi(endpoint, ...parametrosExtras) {
  const [dados, setDados] = React.useState(null);
  const [carregando, setCarregando] = React.useState(true);

  React.useEffect(() => {
    const buscarDados = async () => {
      const query = criarQueryString(
        { limite: 10, pagina: 1 },
        ...parametrosExtras
      );

      const resposta = await fetch(`/api/${endpoint}?${query}`);
      const resultado = await resposta.json();
      setDados(resultado);
      setCarregando(false);
    };

    buscarDados();
  }, [endpoint, ...parametrosExtras]);

  return { dados, carregando };
}

// Uso no componente
function ListaUsuarios() {
  const { dados, carregando } = useApi("usuarios", { ativo: true }, { cargo: "admin" });

  if (carregando) return <div>Carregando...</div>;
  return <pre>{JSON.stringify(dados, null, 2)}</pre>;
}

Refatoração de código legado usando operadores modernos:

// Código legado
function criarUsuario(nome, idade, email, telefone, endereco) {
  return {
    nome: nome || "Anônimo",
    idade: idade || 0,
    email: email || "sem@email.com",
    telefone: telefone || "não informado",
    endereco: endereco || {}
  };
}

// Refatorado com parâmetros padrão, rest e spread
function criarUsuario(
  nome = "Anônimo",
  idade = 0,
  ...outros
) {
  const [email = "sem@email.com", telefone = "não informado", endereco = {}] = outros;

  return {
    nome,
    idade,
    email,
    telefone,
    ...endereco,
    contato: { email, telefone }
  };
}

Referências