Módulos ES6

Uno de los principales problemas que ha ido arrastrando Javascript desde sus inicios es la dificultad de organizar de una forma adecuada una aplicación grande, con muchas líneas de código. En muchos lenguajes, cuando un programa crece, se comienza a estructurar en funciones y, posteriormente, en clases. De esta forma organizamos de forma más lógica el código de nuestro programa.

Pero tener todo el código en un sólo fichero Javascript también se vuelve confuso y complejo de organizar. En la mayoría de los lenguajes de programación, las clases se dividen en ficheros diferentes, de modo que cada clase está localizada en un fichero separado. Esto en el «lado del cliente» (como es el caso de Javascript en el navegador) presenta varias problemáticas.

Funciones, clases y ficheros (módulos)

En un principio, y de forma nativa, la forma más extendida era incluir varias etiquetas <script> desde nuestra página HTML. De esta forma, podíamos tener varios ficheros Javascript separados, cada uno para una finalidad concreta. Sin embargo, este sistema es poco modular, ofrecía un control pobre y resultaba lento, ya que sobrecargaba al cliente con múltiples peticiones extra.

Con el tiempo, se desarrollaron otros sistemas como CommonJS (utilizado en NodeJS) o RequireJS (AMD), cada uno con sus particularidades, virtudes y desventajas.

¿Qué son los módulos ES?

En ECMAScript 6 se introduce una característica nativa denominada Módulos ES6, que permite la importación y exportación de código entre diferentes ficheros Javascript, eliminando las desventajas que teníamos hasta ahora y permitiendo trabajar de forma más flexible desde el código Javascript.

Para trabajar con módulos tenemos a nuestra disposición las siguientes palabras clave:

Declaración Descripción
export Exporta uno o varios elementos (variables, funciones, clases...) del fichero actual
import Importa uno o varios elementos (variables, funciones, clases...) desde otro fichero .js

Mediante la palabra clave export crearemos un objeto (módulo de exportación) que contendrá una o varias propiedades. En estas propiedades podremos guardar variables, funciones o clases (a partir de ahora, elementos). Si dicho módulo ya existe, podremos ir añadiendo más propiedades. Por otro lado, con la palabra clave import podremos leer dichos módulos de otros ficheros y utilizar sus propiedades en nuestro código.

Exportación de módulos

Por defecto, un fichero Javascript no tiene módulo de exportación si no se usa un export al menos una vez. Existen varias formas de exportar código mediante la palabra clave export:

Forma Descripción
export { name }; Añade el elemento name al módulo de exportación.
export { n1, n2, n3... }; Añade los elementos indicados (n1, n2, n3...) al módulo de exportación.
export * from './file.js'; Añade todos los elementos del módulo file.js al módulo de exportación.
export declaration; Declara una variable, función o clase y la añade al módulo de exportación.
export default declaration; Declara una función o clase y la añade al módulo de exportación.

Es posible renombrar los elementos sobre la marcha utilizando as seguido del nuevo nombre. Además, si se indica default como nuevo nombre, ese elemento será la exportación por defecto. Sólo puede haber una exportación por defecto por fichero.

Veamos algunos ejemplos:

let number = 4;
const saludar = () => "¡Hola!";
const goodbye = () => "¡Adiós!";
class Clase {
}

export { number };                          // Se crea un módulo y se añade number
export { saludar, goodbye as despedir };    // Se añade saludar y despedir al módulo
export { Clase as default };                // Se añade Clase al módulo (default)
export { saludar as otroNombre };           // Se añade otroNombre al módulo

También es posible exportar variables, funciones o clases simplemente incluyendo la palabra export a la izquierda de la línea de su declaración:

export const f1 = () => 42;                       // Se crea un módulo y se añade f1
export default function f2() { return "Manz"; };  // Se añade f2 al módulo (default)

Ten en cuenta que en el caso de utilizar una exportación por defecto en una declaración, no es posible utilizar var, let o const. Tampoco es posible usar export dentro de funciones, bucles o contextos específicos.

Importación de módulos

Si por un lado tenemos export, la palabra clave import es su opuesta. Con ella podemos cargar un módulo de exportación de otro fichero Javascript, con todos los elementos exportados que contiene.

Existen varias formas de importar código utilizando import:

Forma Descripción
import nombre from './file.js'; Importa sólo el elemento por defecto de file.js en nombre.
import { nombre } from './file.js'; Importa sólo el elemento nombre de file.js.
import { n1, n2.. } from './file.js'; Importa los elementos indicados desde file.js.
import * as obj from './file.js'; Importa todos los elementos de file.js en el objeto obj.
import './file.js'; No importa elementos, pero ejecuta el código de file.js.

Recuerda, que al igual que con la exportación, también puedes renombrar elementos utilizando as seguido del nuevo nombre.

En el primer caso, importamos el elemento por defecto desde el módulo file.js y lo guardamos en la variable nombre. En el segundo y tercer caso, importamos los elementos indicados en el interior de los corchetes, desde el módulo file.js.

En el cuarto caso, importamos todos los elementos del módulo externo file.js en un objeto de nombre obj (es obligatorio indicar el nombre) y en el quinto caso, no importamos elementos, pero leemos el código del módulo y lo ejecutamos.

Convenciones de módulos ES6

  • Si queremos utilizar import y export desde el navegador directamente, deberemos añadir los archivos con módulos con la etiqueta <script> utilizando el atributo type="module". Estas etiquetas de módulos se cargan en diferido, o lo que es lo mismo, como si fueran un <script defer>:
<script type="module" src="file.mjs"></script>
  • Por norma general, a los archivos Javascript con módulos se les suele indicar la extensión .mjs. Aunque también se podría hacer especificando otra extensión como .es6 o .js.

  • Se aconseja utilizar las rutas UNIX en los export e import, ya que son las que tienen mejor soporte, tanto en navegadores como en NodeJS. También se pueden indicar rutas absolutas (ten en cuenta el tiempo de carga extra):

// Incorrecto
import { elemento } from 'module.mjs';
import { elemento } from 'folder/module.mjs';

// Correcto
import { elemento } from './module.mjs';    // misma carpeta del .js
import { elemento } from '/module.mjs';     // carpeta raíz
import { elemento } from '../module.mjs';   // carpeta anterior al .js
import { ceil } from 'https://unpkg.com/[email protected]/lodash.js';
  • Se aconseja realizar las exportaciones al final de los ficheros Javascript. Aunque no es obligatorio, esto mejora la legibilidad de código, ya que siempre te esperas que los export aparezcan al final del código.
Manz
Publicado por Manz

Docente, divulgador informático y freelance. Escribe en Emezeta.com, es profesor en la Oficina de Software Libre de la Universidad de La Laguna y dirige el curso de Programación web FullStack de EOI en Tenerife (Canarias). En sus ratos libres, busca GIF de gatos en Internet.