Módulos ES6
Introducción
Los módulos ES6 representan una de las características más importantes introducidas en ECMAScript 2015 (ES6), ya que proporcionan un mecanismo nativo para organizar y compartir código entre diferentes archivos JavaScript. Antes de su introducción, los desarrolladores utilizaban diferentes patrones y bibliotecas externas para simular la modularidad, lo que frecuentemente resultaba en código menos mantenible y propenso a errores. Con los módulos ES6, JavaScript incorpora finalmente un sistema de módulos estandarizado directamente en el lenguaje, permitiendo estructurar aplicaciones complejas de manera más clara y profesional.
En este artículo aprenderás a utilizar el sistema de módulos ES6, incluyendo cómo importar y exportar código, las diferencias entre exportaciones por defecto y nombradas, y técnicas avanzadas como las importaciones dinámicas. Dominar los módulos ES6 te permitirá construir aplicaciones JavaScript mejor organizadas, más mantenibles y escalables.
Sintaxis de import y export
Los módulos ES6 se basan principalmente en dos declaraciones: export
para compartir código desde un módulo e import
para utilizar código de otros módulos.
Exportaciones básicas
Para hacer que una función, variable o clase esté disponible fuera de su archivo, usamos la palabra clave export
:
// matematicas.js
// Exportando funciones individuales
export function sumar(a, b) {
return a + b;
}
export function restar(a, b) {
return a - b;
}
// Variables que también podemos exportar
export const PI = 3.14159;
También es posible declarar primero los elementos y exportarlos después:
// matematicas.js
function multiplicar(a, b) {
return a * b;
}
function dividir(a, b) {
if (b === 0) {
throw new Error("No se puede dividir por cero");
}
return a / b;
}
const E = 2.71828;
// Exportación agrupada al final
export { multiplicar, dividir, E };
Importaciones básicas
Para utilizar código exportado desde otros módulos, usamos la palabra clave import
:
// calculadora.js
// Importamos elementos específicos del módulo matemáticas
import { sumar, restar, PI } from './matematicas.js';
console.log(sumar(5, 3)); // 8
console.log(restar(10, 4)); // 6
console.log(PI); // 3.14159
Exportaciones por defecto vs. nombradas
Los módulos ES6 ofrecen dos tipos de exportaciones: nombradas y por defecto.
Exportación por defecto
La exportación por defecto permite designar un valor principal para el módulo. Cada archivo puede tener solo una exportación por defecto:
// usuario.js
class Usuario {
constructor(nombre, email) {
this.nombre = nombre;
this.email = email;
}
saludar() {
return `¡Hola, soy ${this.nombre}!`;
}
}
// Solo puede haber una exportación por defecto por archivo
export default Usuario;
Para importar una exportación por defecto:
// app.js
// No necesitamos llaves {} y podemos usar cualquier nombre
import User from './usuario.js';
const usuario = new User('Ana', 'ana@ejemplo.com');
console.log(usuario.saludar()); // ¡Hola, soy Ana!
Exportaciones nombradas
Las exportaciones nombradas permiten exportar múltiples valores desde un módulo:
// validaciones.js
export function validarEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
export function validarPassword(password) {
return password.length >= 8;
}
Para importar exportaciones nombradas:
// formulario.js
// Debemos usar exactamente el mismo nombre y con llaves {}
import { validarEmail, validarPassword } from './validaciones.js';
console.log(validarEmail('test@example.com')); // true
console.log(validarPassword('abc123')); // false (menos de 8 caracteres)
Importación selectiva de componentes
Podemos importar solo los componentes específicos que necesitamos de un módulo, lo que hace nuestro código más eficiente:
// Solo importamos lo que necesitamos
import { sumar, PI } from './matematicas.js';
// Usamos solo lo que importamos
console.log(sumar(PI, 10)); // 13.14159
Alias en importaciones y exportaciones
Podemos utilizar alias para renombrar los elementos durante la importación o exportación, lo que nos ayuda a evitar conflictos de nombres o a simplificar nombres largos.
Alias en exportaciones
// utilidades.js
function obtenerFechaActualFormateada() {
const fecha = new Date();
return `${fecha.getDate()}/${fecha.getMonth() + 1}/${fecha.getFullYear()}`;
}
// Exportamos con un alias más corto
export { obtenerFechaActualFormateada as fechaActual };
Alias en importaciones
// dashboard.js
// Importamos usando un alias
import { validarEmail as validarCorreo } from './validaciones.js';
import { fechaActual } from './utilidades.js';
console.log(validarCorreo('usuario@dominio.com')); // true
console.log(fechaActual()); // Ejemplo: 18/4/2025
Importaciones dinámicas
Las importaciones dinámicas permiten cargar módulos bajo demanda, lo que puede mejorar significativamente el rendimiento de nuestra aplicación al cargar solo lo necesario cuando se necesita.
// app.js
const cargarModuloDeIdioma = async (idioma) => {
try {
// Importación dinámica basada en una variable
const modulo = await import(`./idiomas/${idioma}.js`);
return modulo.default || modulo;
} catch (error) {
console.error('Error al cargar el módulo:', error);
// Cargamos un idioma por defecto si falla
const moduloPorDefecto = await import('./idiomas/es.js');
return moduloPorDefecto.default || moduloPorDefecto;
}
};
// Uso de la importación dinámica
document.getElementById('cambiarIdioma').addEventListener('click', async () => {
const idioma = document.getElementById('selectIdioma').value;
const moduloIdioma = await cargarModuloDeIdioma(idioma);
document.getElementById('saludo').textContent = moduloIdioma.saludo;
});
En este ejemplo, cargamos diferentes módulos de idioma dependiendo de la selección del usuario. Esto es especialmente útil para aplicaciones grandes donde no queremos cargar todo el código de una vez.
Ámbito de módulo
Cada módulo ES6 tiene su propio ámbito léxico, lo que significa que las variables, funciones y clases definidas en un módulo no están disponibles globalmente a menos que se exporten explícitamente.
// config.js
// Esta variable solo está disponible dentro de este módulo
const URL_API = 'https://api.ejemplo.com/v1';
// Esta función está disponible para otros módulos
export function obtenerUrlCompleta(endpoint) {
return `${URL_API}/${endpoint}`;
}
// El resto del código no puede acceder a URL_API directamente
Esto proporciona un nivel de encapsulamiento similar al que encontramos en otros lenguajes orientados a objetos, ayudando a prevenir conflictos de nombres y a proteger detalles de implementación.
Compatibilidad entre navegadores
La compatibilidad con módulos ES6 varía entre navegadores, especialmente en versiones antiguas. Para utilizar módulos ES6 directamente en el navegador:
<!-- index.html -->
<script type="module" src="./app.js"></script>
El atributo type="module"
indica al navegador que el script debe tratarse como un módulo ES6. Los módulos se ejecutan en modo estricto por defecto y son tratados como diferidos (similar al atributo defer
).
Organización de proyectos con módulos ES6
Los módulos ES6 permiten diferentes estrategias de organización para proyectos JavaScript:
Enfoque por funcionalidad
Organizando los módulos por la funcionalidad que proporcionan:
proyecto/
├── src/
│ ├── auth/
│ │ ├── login.js
│ │ ├── registro.js
│ │ └── index.js
│ ├── utils/
│ │ ├── formateo.js
│ │ ├── validacion.js
│ │ └── index.js
│ └── app.js
Archivos de barril (barrel files)
Los "archivos de barril" son módulos que re-exportan desde varios otros módulos, simplificando las importaciones:
// utils/index.js
// Re-exportamos todo desde los módulos individuales
export * from './formateo.js';
export * from './validacion.js';
Esto nos permite importar de manera más limpia:
// Antes
import { formatearFecha } from './utils/formateo.js';
import { validarEmail } from './utils/validacion.js';
// Después, con archivos de barril
import { formatearFecha, validarEmail } from './utils';
Resumen
Los módulos ES6 representan un avance fundamental en JavaScript, proporcionando un sistema de módulos nativo que permite organizar el código de manera más estructurada y mantenible. Con la sintaxis de import
y export
, podemos compartir código entre archivos de manera clara y organizada, evitando la contaminación del ámbito global y facilitando la creación de aplicaciones complejas.
A través de características como las exportaciones por defecto y nombradas, los alias, y las importaciones dinámicas, los módulos ES6 ofrecen una gran flexibilidad para diferentes escenarios de desarrollo. En la actualidad, estos módulos son ampliamente compatibles con los navegadores modernos y, con las herramientas adecuadas, pueden utilizarse incluso en entornos más antiguos, convirtiéndose en una parte esencial del desarrollo JavaScript moderno.