Funções: declaração, expressão e hoisting
1. Fundamentos das Funções em JavaScript
Funções são blocos de código reutilizáveis que executam uma tarefa específica. Em JavaScript, elas são tratadas como cidadãos de primeira classe (first-class citizens), o que significa que podem ser atribuídas a variáveis, passadas como argumentos para outras funções, retornadas de outras funções e armazenadas em estruturas de dados.
A diferença fundamental entre função e método está no contexto: uma função é independente, enquanto um método é uma função associada a um objeto (incluindo protótipos e classes). No React, por exemplo, métodos de ciclo de vida em componentes de classe são métodos, enquanto hooks como useEffect recebem funções como argumentos.
// Função independente
function somar(a, b) {
return a + b;
}
// Método (função associada a um objeto)
const calculadora = {
somar: function(a, b) {
return a + b;
}
};
2. Declaração de Função (Function Declaration)
A declaração de função utiliza a sintaxe function nomeDaFuncao() {}. O nome é obrigatório e o recurso de hoisting eleva a declaração completa para o topo do escopo, permitindo que a função seja chamada antes de sua definição no código.
// Chamada antes da declaração (funciona graças ao hoisting)
console.log(saudacao('Maria')); // "Olá, Maria!"
function saudacao(nome) {
return `Olá, ${nome}!`;
}
Em Node.js, declarações de função são comuns em módulos para exportar funcionalidades:
// utils.js
function formatarData(data) {
return data.toLocaleDateString('pt-BR');
}
function validarEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
module.exports = { formatarData, validarEmail };
Em React, componentes funcionais são declarados com function declaration ou arrow function:
// Componente funcional com function declaration
function Card({ titulo, descricao }) {
return (
<div className="card">
<h2>{titulo}</h2>
<p>{descricao}</p>
</div>
);
}
3. Expressão de Função (Function Expression)
A expressão de função atribui uma função (geralmente anônima) a uma variável. Diferente da declaração, a função não é içada — apenas a variável é elevada, mas seu valor permanece undefined até a execução da atribuição.
// Função anônima atribuída a uma variável
const saudacao = function(nome) {
return `Olá, ${nome}!`;
};
// Função nomeada em expressão (útil para debug em stack traces)
const saudacao = function saudacaoCompleta(nome) {
return `Olá, ${nome}!`;
};
Em Node.js, expressões de função são amplamente usadas em callbacks:
const fs = require('fs');
fs.readFile('dados.txt', 'utf8', function(err, data) {
if (err) {
console.error('Erro ao ler arquivo:', err);
return;
}
console.log('Conteúdo:', data);
});
Em React, handlers de eventos frequentemente usam expressões de função:
function BotaoContador() {
const [contador, setContador] = useState(0);
// Expressão de função como handler
const incrementar = function() {
setContador(prev => prev + 1);
};
return <button onClick={incrementar}>Clique {contador}</button>;
}
4. Hoisting: Como o JavaScript "Eleva" as Funções
Hoisting é o comportamento do JavaScript de mover declarações para o topo do escopo durante a fase de compilação. Com function declarations, tanto a declaração quanto o corpo da função são içados:
console.log(teste()); // "Funciona!"
function teste() {
return "Funciona!";
}
Com function expressions, apenas a variável é içada (com valor undefined), não a função:
console.log(teste); // undefined
console.log(teste()); // TypeError: teste is not a function
var teste = function() {
return "Não funciona!";
};
O comportamento é idêntico no Node.js e no navegador, pois ambos utilizam a engine V8:
// Node.js
console.log(typeof minhaFuncao); // "function" (declaração içada)
function minhaFuncao() {}
console.log(typeof minhaExpressao); // "undefined" (apenas variável içada)
var minhaExpressao = function() {};
5. Diferenças Práticas entre Declaração e Expressão
A principal diferença prática é a ordem de chamada. Declarações permitem chamadas antes da definição; expressões não.
Escopo em bloco com let/const e strict mode:
'use strict';
if (true) {
function declaracao() { return 'declaracao'; } // Comportamento variável entre engines
const expressao = function() { return 'expressao'; };
}
// declaracao(); // Pode ou não funcionar dependendo do ambiente
// expressao(); // ReferenceError: expressao is not defined
Casos de uso recomendados:
- Declaração: funções utilitárias, componentes principais, APIs públicas de módulos
- Expressão: callbacks, closures, funções condicionais, IIFEs (Immediately Invoked Function Expressions)
6. Arrow Functions vs Funções Tradicionais
Arrow functions são uma sintaxe concisa para expressões de função. Elas não têm seu próprio this, arguments, super ou new.target, e não podem ser usadas como construtores.
// Arrow function como expressão
const quadrado = x => x * x;
// Arrow function com bloco
const somar = (a, b) => {
return a + b;
};
O hoisting com arrow functions é idêntico ao de expressões de função:
console.log(minhaArrow); // undefined
console.log(minhaArrow()); // TypeError: minhaArrow is not a function
var minhaArrow = () => "Arrow function";
Em React, arrow functions são preferidas para:
- Componentes funcionais: sintaxe mais limpa
- Hooks: callbacks em useEffect, useCallback
- Handlers: evitar problemas com this em componentes de classe
// Componente com arrow function
const Lista = ({ itens }) => {
return (
<ul>
{itens.map(item => (
<li key={item.id}>{item.nome}</li>
))}
</ul>
);
};
// Hook useEffect com arrow function
useEffect(() => {
fetchDados();
}, []);
7. Boas Práticas em Projetos Node.js + React
Organização de funções em módulos Node.js:
// utils/validacao.js
export function validarCPF(cpf) { /* ... */ }
export function validarCNPJ(cnpj) { /* ... */ }
// Uso
import { validarCPF } from './utils/validacao.js';
Declaração de funções em componentes React:
// Função auxiliar fora do componente (sem hoisting problemático)
function formatarNomeCompleto(nome, sobrenome) {
return `${nome} ${sobrenome}`.trim();
}
function Perfil({ usuario }) {
// Função interna ao componente (recriada a cada render)
const handleClick = () => {
console.log(formatarNomeCompleto(usuario.nome, usuario.sobrenome));
};
return <button onClick={handleClick}>Mostrar nome</button>;
}
Evitando problemas de hoisting em código assíncrono:
// PROBLEMÁTICO: hoisting pode causar confusão
processarDados(); // Funciona? Depende...
async function processarDados() {
const resultado = await fetch('/api/dados');
console.log(resultado);
}
// RECOMENDADO: usar const com arrow function
const processarDados = async () => {
const resultado = await fetch('/api/dados');
console.log(resultado);
};
8. Exemplos Práticos e Debugging
Debugando hoisting no Node.js:
// demo.js
console.log('1. Antes da declaração:');
console.log(' funcaoDeclarada:', typeof funcaoDeclarada);
console.log(' funcaoExpressao:', typeof funcaoExpressao);
function funcaoDeclarada() { return 'sou uma declaração'; }
var funcaoExpressao = function() { return 'sou uma expressão'; };
console.log('2. Após a declaração:');
console.log(' funcaoDeclarada:', typeof funcaoDeclarada);
console.log(' funcaoExpressao:', typeof funcaoExpressao);
Erro comum: Temporal Dead Zone (TDZ) com let e const:
// ReferenceError: Cannot access 'minhaFuncao' before initialization
console.log(minhaFuncao());
const minhaFuncao = () => 'arrow function';
Ferramentas para debugging:
- Node.js debugger: node inspect script.js
- React DevTools: componente "Components" para inspecionar props e estado
- Chrome DevTools: Sources > Call Stack para rastrear execução de funções
// Exemplo para debug no Node.js
function calcularMedia(numeros) {
debugger; // Ponto de parada
const soma = numeros.reduce((acc, n) => acc + n, 0);
return soma / numeros.length;
}
calcularMedia([10, 20, 30]);
Referências
- MDN Web Docs: Functions — Guia completo sobre funções em JavaScript, incluindo declaração, expressão e hoisting
- MDN Web Docs: Hoisting — Explicação detalhada do conceito de hoisting em JavaScript
- JavaScript.info: Function Expressions — Tutorial interativo sobre expressões de função e diferenças para declarações
- Node.js Documentation: Modules — Documentação oficial sobre organização de funções em módulos Node.js
- React Documentation: Components and Props — Guia oficial sobre criação de componentes funcionais em React
- W3Schools: JavaScript Function Definitions — Tutorial prático com exemplos de declaração, expressão e arrow functions
- DigitalOcean: Understanding Hoisting in JavaScript — Artigo técnico explicando hoisting com exemplos práticos