Os módulos ES (ECMAScript Modules) são uma funcionalidade moderna do JavaScript que permite dividir o código em arquivos independentes e reutilizáveis. Antes dos módulos, o JavaScript carecia de um sistema nativo de organização de código, o que levava a soluções como IIFEs e bibliotecas externas (CommonJS, AMD). Com os módulos ES, você pode exportar e importar funcionalidades de forma explícita, melhorando a manutenibilidade, o encapsulamento e a reutilização de código.

Nesta aula, vamos explorar os dois tipos de exportação (nomeada e padrão), como importar módulos, e as diferenças de uso no navegador e no Node.js. Ao final, você terá uma base sólida para estruturar projetos JavaScript modernos usando módulos.

export nomeado e default

Em JavaScript, você pode exportar declarações de duas maneiras: exportações nomeadas e exportação padrão. Cada uma tem suas particularidades e usos recomendados.

Exportações nomeadas permitem exportar múltiplos valores de um módulo, cada um com um nome específico. Isso é útil quando você tem várias funções, constantes ou classes que fazem sentido serem agrupadas. Para exportar, use a palavra-chave export antes da declaração, ou use uma lista de exportações no final do arquivo.

Exportação padrão é única por módulo e geralmente representa o "principal" item exportado, como uma classe ou função principal. Você pode exportar apenas um valor padrão por módulo, usando export default.

A escolha entre nomeada e padrão depende do design do seu módulo. Exportações nomeadas são mais explícitas e facilitam a refatoração, enquanto a exportação padrão é conveniente quando você deseja que o importador possa nomear o valor de forma arbitrária.

Vejamos exemplos práticos. Crie um arquivo math.js com exportações nomeadas:

// math.js
// Exportação nomeada: função somar
export function somar(a, b) {
  return a + b;
}

// Exportação nomeada: constante PI
export const PI = 3.14159;

// Também é possível exportar no final
function subtrair(a, b) {
  return a - b;
}

const E = 2.71828;

export { subtrair, E };

Agora, um exemplo de exportação padrão. Crie utils.js:

// utils.js
// Exportação padrão: uma função principal
export default function formatarData(data) {
  return data.toISOString().split('T')[0];
}

// Também é possível exportar nomeados junto com o padrão
export function saudacao(nome) {
  return `Olá, ${nome}!`;
}

Em um módulo, você pode combinar exportações nomeadas e padrão, mas lembre-se: apenas uma exportação padrão é permitida.

import

Para usar os valores exportados, utilizamos a declaração import. A sintaxe varia conforme o tipo de exportação (nomeada ou padrão).

Importação de exportações nomeadas: você deve usar chaves e especificar os nomes exatos que deseja importar. Você também pode usar aliases (as) para renomear.

Importação da exportação padrão: você pode dar qualquer nome, pois é único. A sintaxe não usa chaves.

É possível importar tanto a exportação padrão quanto as nomeadas no mesmo comando, separando por vírgula.

Vejamos como importar do arquivo math.js e utils.js:

// main.js
// Importando exportações nomeadas
import { somar, PI, subtrair, E } from './math.js';

// Importando com alias
import { somar as soma } from './math.js';

// Importando a exportação padrão
import formatarData from './utils.js';

// Importando padrão e nomeados juntos
import formatarData, { saudacao } from './utils.js';

// Usando os imports
console.log(somar(2, 3)); // 5
console.log(PI); // 3.14159
console.log(formatarData(new Date())); // 2025-01-01
console.log(saudacao('Maria')); // Olá, Maria!

Note que a extensão .js é necessária no import para módulos ES no navegador e no Node.js (quando usando ES modules). Em bundlers como Webpack, pode ser omitida, mas é boa prática incluí-la.

Módulos no browser e no Node

Os módulos ES são suportados nativamente em navegadores modernos e no Node.js (a partir da versão 12, com suporte estável). No entanto, existem diferenças importantes em como carregar e usar módulos nesses ambientes.

No navegador: para usar módulos, você deve incluir o atributo type="module" na tag <script>. Os módulos são carregados de forma assíncrona e são executados em modo estrito. Além disso, os módulos têm escopo próprio, então variáveis declaradas dentro de um módulo não são globais.

Exemplo de uso no navegador:

<!-- index.html -->
<script type="module" src="main.js"></script>

E no main.js você pode importar outros módulos normalmente.

No Node.js: o suporte a ES modules é ativado de duas maneiras: ou você usa a extensão .mjs para seus arquivos, ou define "type": "module" no package.json mais próximo. Isso faz com que o Node trate os arquivos .js como ES modules. Sem isso, o Node usa CommonJS por padrão.

Exemplo de configuração no package.json:

{
  "type": "module"
}

Depois, você pode usar import/export normalmente nos arquivos .js.

Uma diferença importante é que, no Node, o caminho do módulo deve incluir a extensão do arquivo (por exemplo, ./math.js), mas no navegador também é necessário. Além disso, no Node, você pode importar módulos de pacotes instalados via npm, como import express from 'express';.

Vejamos um exemplo completo de módulo no Node:

// arquivo: saludo.js
export function hola(nombre) {
  return `Hola, ${nombre}`;
}
// arquivo: index.js (com type: module no package.json)
import { hola } from './saludo.js';
console.log(hola('Mundo'));

Boas práticas

Ao usar módulos ES, siga estas boas práticas:

  • Prefira exportações nomeadas para múltiplos valores, pois elas tornam as dependências explícitas e facilitam a análise estática.
  • Use exportação padrão apenas para o principal item do módulo, como uma classe ou função principal.
  • Mantenha os módulos pequenos e focados em uma única responsabilidade.
  • Evite efeitos colaterais em módulos (como alterar globais) para facilitar o teste e a reutilização.
  • Use aliases em imports apenas quando houver conflitos de nomes, para manter a clareza.

Referências

Exercícios

  1. Crie um módulo chamado operacoes.js que exporte as funções soma, subtracao e multiplicacao como exportações nomeadas. Em seguida, importe-as em um arquivo main.js e use-as para calcular 3 + 5, 10 - 4 e 7 * 2, exibindo os resultados no console.

    ✓ Resposta:
    // operacoes.js
    export function soma(a, b) { return a + b; }
    export function subtracao(a, b) { return a - b; }
    export function multiplicacao(a, b) { return a * b; }
    
    // main.js
    import { soma, subtracao, multiplicacao } from './operacoes.js';
    console.log(soma(3, 5)); // 8
    console.log(subtracao(10, 4)); // 6
    console.log(multiplicacao(7, 2)); // 14
    
  2. Em um módulo pessoa.js, exporte uma classe Pessoa com propriedades nome e idade, e um método apresentar() que retorna uma string como "Olá, meu nome é [nome] e tenho [idade] anos.". Use exportação padrão. Importe a classe em main.js e crie uma instância para exibir a apresentação.

    ✓ Resposta:
    // pessoa.js
    export default class Pessoa {
      constructor(nome, idade) {
        this.nome = nome;
        this.idade = idade;
      }
      apresentar() {
        return `Olá, meu nome é ${this.nome} e tenho ${this.idade} anos.`;
      }
    }
    
    // main.js
    import Pessoa from './pessoa.js';
    const pessoa = new Pessoa('Ana', 25);
    console.log(pessoa.apresentar());
    
  3. Explique a diferença entre exportação nomeada e exportação padrão, e dê um exemplo de quando você usaria cada uma.

    ✓ Resposta:

    Exportação nomeada permite exportar múltiplos valores com nomes específicos, exigindo que o importador use os mesmos nomes (ou aliases). É útil quando o módulo tem várias funções relacionadas, como utilitários. Exportação padrão é única por módulo e permite que o importador nomeie o valor como quiser; é adequada quando o módulo principal é uma única classe ou função, como um componente React ou uma classe de serviço.

  4. Como você ativa o suporte a ES modules no Node.js? Dê duas maneiras e explique a diferença.

    ✓ Resposta:

    Duas maneiras: 1) Usar a extensão .mjs para os arquivos que contêm módulos ES; 2) Definir "type": "module" no package.json mais próximo, fazendo com que todos os arquivos .js sejam tratados como ES modules. A diferença é que com .mjs você controla explicitamente quais arquivos são módulos, enquanto com "type": "module" todos os .js no diretório (e subdiretórios) são módulos, a menos que tenham extensão .cjs.

  5. No navegador, qual atributo é necessário na tag <script> para usar módulos? Escreva um exemplo de HTML que carrega um módulo chamado app.js.

    ✓ Resposta:

    É necessário o atributo type="module". Exemplo:

    <script type="module" src="app.js"></script>
    

Observações finais

Os módulos ES são uma ferramenta poderosa para organizar código em projetos JavaScript modernos. Eles são suportados em todos os navegadores atuais e no Node.js, e são a base para frameworks como React, Vue e Angular. Dominar import/export é essencial para trabalhar com bundlers, testar código e manter uma base de código limpa.

Lembre-se de que a ordem de carregamento dos módulos é assíncrona no navegador, então não dependa de scripts que não são módulos para acessar variáveis de módulos. Além disso, módulos são executados em modo estrito, então variáveis não declaradas causarão erros.

Agora você está pronto para estruturar seus projetos com módulos ES. Pratique criando seus próprios módulos e explorando as possibilidades.