Uso de módulos en JavaScript con ECMAScript 6
JavaScript no tiene (por ahora) un sistema de módulos propio, aunque eso si, la comunidad fue creando varios para suplir esa necesidad. Actualmente hay dos importantes propuestas (incompatibles entre sí):
CommonJS: Esta es la implementación usada en Node.js y Browserify, se caracteriza por una sintaxis sencilla, carga los módulos de forma síncrona y se usa principalmente en el servidor.
Definición de Módulos Asíncronos (AMD): La implementación más popular de este estándar es RequireJS, se caracteriza por una sintaxis un poco más complicada, estar diseñado para cargar módulos de forma asíncrona y se usa principalmente en navegadores.
Más documentación sobre estos sistemas: https://addyosmani.com/writing-modular-js/
Nota: El soporte para módulos de ECMAScript 6 en los navegadores actualmente es nulo, esto está todavía en desarrollo y no está listo para ser usado, hay sin embargo formas de escribir código usando ES6 y luego convertir el código a ES5 quedando un resultado bastante bueno, pero no es un soporte nativo, sigue siendo ES5.
La meta final de los módulos de ECMAScript 6 fue crear un formato que dejara contento a los usuarios de CommonJS y AMD.
- Similitudes con CommonJS: sintaxis sencilla, preferencia por un solo export y soportar dependencias cíclicas.
- Similitudes con AMD: soporte para carga asíncrona y configurable de módulos.
Al estar el sistema incorporado directamente en el lenguaje, permite que sea mejor que CommonJS y AMD.
- La sintaxis es incluso más simple que CommonJS.
- La estructura puede ser analizada estaticamente (para comprobaciones, optimizaciones, etc.).
- Tiene un mejor soporte para dependencias cíclicas que CommonJS.
El estándar de módulo de ES6 se divido en dos partes:
- La sintaxis para exportar e importar.
- La API de carga programable, para configurar como se cargan los módulos y cargarlos en diferentes condiciones.
Visión general de la sintaxis
Hay dos formas de exportar: exportación con nombre (varias por módulo) o por defecto (una por módulo).
Exportación con nombre (varias por módulo)
Un módulo puede exportar múltiples cosas agregandole la palabra export antes de la declaración. Estas exportaciones se distinguen por su nombre.
//------ calculos.js ------
export function sumar(x, y) {
return x + y;
}
export function restar(x, y) {
return x - y;
}
//------ main.js ------
import { sumar, restar } from "calculos";
console.log(sumar(2, 3)); // 5
console.log(restar(4, 3)); // 1
Como se ve es bastante simple, solo escribes el código como si no hubiera nada que lo englobe (sin scope global) y a todo lo que desees exportar solo le agregas la palabra export.
Si lo deseas también puedes importar un módulo entero y acceder a las exportaciones con nombre usando la sintaxis de propiedades:
//------ main.js ------
import * as calc from "calculos";
console.log(calc.sumar(2, 3)); // 7
console.log(calc.restar(4, 3)); // 1
Exportación por defecto (una por módulo)
Los módulos que solo exporten un único valor son muy populares en la comunidad de Node.js y en el desarrollo frontend. Un módulo de ES6 puede tomar un valor por defecto en la exportación.
//------ miFunc.js ------
export default function () { ... };
//------ main1.js ------
import miFunc from "miFunc";
miFunc();
Nota: El valor a exportar por defecto es una expresión y no tiene nombre, este es obtenido al importar el módulo, normalmente se usaría el nombre del módulo.
Beneficio de los módulos de ECMAScript 6
A primera vista, tener módulos de forma nativa en ES6 puede parecer una funcionalidad inútil, después de todo ya existe varios sistemas de módulos muy buenos. Pero los módulos de ES6 tienen características que no se pueden agregar con una librería, como una sintaxis reducida y una estructura estática de modulos (lo que ayuda a optimizar entre otras cosas). Además se espera que termine con la fragmentación entre los sistemas CommonsJS y AMD.
Tener un único estandar para módulos nativo significa:
- No más UMD (Definición Universal de Módulos): UMD es un patrón que permite que un mismo archivo sea usado por distintos sistemas (como CommonJS y AMD). Cuando ES6 este listo para su uso UMD se volverá obsoleto.
- Las nuevas API de los navegadores se volveran módulos en lugar de variables globales o propiedades como navigator.
- No más objetos como namespaces. Objetos como Math y JSON sirven como namespaces para funciones en ECMAScript 5. En el futuro, esta funcionalidad la darán los módulos.
Referencias (en inglés)
Para finalizar les dejo algunos links (en inglés) con más información sobre los módulos de ES6 para los que quieran aprender un poco más sobre como funcionan (como la carga asíncrona de módulos que no está explicado arriba).