Skip to content

L51 - Módulos ES6

Los módulos ES6 permiten dividir el código JavaScript en archivos independientes que pueden exportar e importar funcionalidades entre sí. Cada módulo tiene su propio scope aislado: las variables, funciones y clases definidas dentro de un módulo no son accesibles desde fuera a menos que se exporten explícitamente.

utils.js
export const PI = 3.1416;
export function sumar(a, b) { return a + b; }
export class Calculadora { ... }

También se pueden declarar primero y exportar después:

const PI = 3.1416;
function sumar(a, b) { return a + b; }
export { PI, sumar };

Con alias:

export { PI as PI_VALUE, sumar as add };

Cada módulo puede tener un único export default. Se usa para lo que el módulo “exporta por defecto”:

logger.js
export default function log(msg) {
console.log(`[LOG] ${msg}`);
}

O una clase:

export default class Usuario { ... }
import { PI, sumar } from './utils.js';
import { PI as PI_VALUE, sumar as add } from './utils.js';
import * as utilidades from './utils.js'; // Namespace
// uso: utilidades.PI, utilidades.sumar()
import log from './logger.js';
import miLog from './logger.js'; // cualquier nombre vale
import log, { PI, sumar } from './logger.js';

Para usar módulos en el navegador, el script debe cargarse con type="module":

<script type="module" src="app.js"></script>
Script clásicoMódulo
Scope globalScope propio del módulo
var crea propiedad en windowvar NO contamina window
Se ejecuta en el orden de cargaSe ejecuta en orden, pero se descargan en paralelo
"use strict" opcionalSiempre en strict mode
this en顶层 es windowthis en顶层 es undefined
Se carga de forma síncrona por defectoSe cargan con defer implícito

import() es una función que devuelve una Promise y permite cargar módulos bajo demanda:

btn.addEventListener('click', async () => {
const modulo = await import('./editor.js');
modulo.iniciarEditor();
});

Casos de uso:

  • Code splitting: dividir el código en chunks que se cargan cuando se necesitan.
  • Carga condicional: importar según el navegador, usuario o característica.
  • Rutas dinámicas en SPAs.
  1. Scope aislado: no hay colisión de nombres entre archivos.
  2. Reutilización: el mismo módulo se puede importar en múltiples lugares.
  3. Tree-shaking: los bundlers (Webpack, Vite) eliminan el código no utilizado.
  4. Mantenibilidad: código organizado en archivos pequeños con responsabilidades claras.
  5. Carga diferida: con import() se cargan solo los recursos necesarios.
config.js
export const API_URL = 'https://api.ejemplo.com';
export const TIMEOUT = 5000;
// api.js
import { API_URL, TIMEOUT } from './config.js';
export async function fetchUsers() {
const res = await fetch(`${API_URL}/users`, {
signal: AbortSignal.timeout(TIMEOUT)
});
return res.json();
}
// app.js
import { fetchUsers } from './api.js';
const users = await fetchUsers();
console.log(users);
  • La ruta en import debe ser relativa (./ o ../) o una URL absoluta — no se puede poner un nombre simple sin ruta (eso requiere bundler).
  • Los módulos se cargan una sola vez, aunque se importen en varios sitios (se cachean).
  • En Node.js, para usar módulos ES6 hay que poner "type": "module" en el package.json o usar extensión .mjs.

🎯 Regla de oro: usa export para exponer solo lo necesario. Mantén el resto privado dentro del módulo.