¿Te gustaría Javascript desde cero?
Tenemos los diplomados que necesitas.¡Haz clic aquí!

 

¿Qué son los módulos en Javascript? ¿Qué es CommonJS, AMD y ECMAScript 6?

Aprende qué son los módulos en JavaScript: qué es CommonJS, AMD, System.js, require.js, ES2015, ECMAScript6 y Webpack.

A medida que JavaScript se usa con mayor frecuencia, los namespaces (espacios de nombres donde conviven los identificadores de nuestra aplicación) y las dependencias se hacen más difíciles de manejar.

Teniendo en cuenta que antes de la llegada de ES6, Javascript no soportaba de forma nativa el uso de módulos, los programadores se las ingenieron para desarrollar sus propios module systems, aprovechando características del mismo lenguaje.

Hoy veremos qué alternativas son las más usadas, y la diferencia entre ellas.

Antes de iniciar: ¿Por qué son necesarios los módulos en Javascript?

Si has desarrollado para otras plataformas, es probable que tengas noción de los conceptos de encapsulación y dependencia.

Años atrás, la gran mayoría de aplicaciones se desarrollaban de forma aislada. Hoy en día, es todo lo contrario.

Es común que alguno de los requerimientos de un sistema que se está desarrollando, se pueda implementar usando como base una solución ya existente.

En el instante en que se introduce un componente ya existente dentro de un nuevo proyecto, se crea una dependencia entre éste proyecto y el componente utilizado.

Dado que estas piezas necesitan trabajar en conjunto, es importante que no existan conflictos entre ellas.

Entonces, si no realizamos ningún tipo de encapsulación, es cuestión de tiempo para que 2 módulos entren en conflicto.

Esta es una de las razones por las que bibliotecas de C usan un prefijo en sus componentes.

La encapsulación es esencial para prevenir conflictos y facilitar el desarrollo.

Cuando se trata de dependencias, en el desarrollo JavaScript de lado del cliente, éstas se han tratado de forma implícita tradicionalmente.

Es decir, siempre ha sido tarea del desarrollador asegurar que las dependencias se satisfagan al momento de ejectar cada bloque de código. Así mismo, asegurar que estas dependencias se carguen en el orden correcto.

A medida que escribimos más código Javascript en nuestras aplicaciones, la gestión de dependencias resulta más engorrosa.

Surgen preguntas como: ¿dónde debemos poner las nuevas dependencias a fin de mantener el orden apropiado?

Los sistemas de módulos (module systems) alivian este problema y otros más. Ellos nacen de la necesidad de “acomodar” el creciente ecosistema de JavaScript.

Veamos qué es lo que aportan las distintas soluciones.

Una primera solución: The Revealing Module Pattern

Antes de la llegada de los module systems:

Un particular patrón de programación comenzó a usarse cada vez con mayor frecuencia en JavaScript: the revealing module pattern o “el patrón del módulo revelador”.

var miModuloRevelador = (function () {
    var nombre = "Guru",
        saludo = "Hola !";

    // Función privada
    function imprimirNombre() {
        console.log("Nombre:" + nombre);
    }

    // Función pública
    function asignarNombre(nuevoNombre) {
        nombre = nuevoNombre;
    }

    // Revelar accesos públicos (opcionalmente con otros nombres)
    return {
        setName: asignarNombre,
        greeting: saludo
    };
})();

miModuloRevelador.setName("TecGurus");

Los ámbitos en Javascript siempre han trabajado a nivel de función (hasta antes de la aparición de let en ES2015).

Esto significa que todo lo que se declara dentro de una función no puede escapar de su ámbito. Es por esta razón que el patrón revealing module se basa en funciones para encapsular el contenido privado (como muchos otros patrones de Javascript).

En el ejemplo anterior, las funciones y variables públicas son expuestas en el objecto devuelto (al final con un return).

Todas las otras declaraciones están protegidas por el ámbito de la función que las contiene.

Debes tener en cuenta que la variable no está recibiendo la función directamente, sino más bien el resultado de ejecutar la función, es decir, el objeto que se devuelve a través del return de la función anónima.

Esto se conoce como “Immediately-invoked function expression”. Si llevas poco tiempo usando Javascript y te parece confuso, te recomiendo que antes de continuar leas este artículo sobre funciones que son invocadas inmediatamente luego de su creación.

PROS

Lo suficientemente simple para ser usado donde sea (no requiere bibliotecas u otro soporte adicional).
Múltiples módulos se pueden definir en un solo archivo.

CONTRAS

No hay forma de importar módulos de forma programada (excepto usando eval).
La dependencias deben gestionarse manualmente.
La carga asíncrona de módulos no es posible.
Las dependencias circulares pueden resultar problemáticas.

CommonJS

CommonJS es un proyecto que define una serie de especificaciones para el ecosistema de Javascript, fuera del navegador (por ejemplo, en el lado del servidor o para aplicaciones de escritorio).

Una de las áreas que el equipo de CommonJS intenta abordar son los módulos en Javascript.

Los desarrolladores de Node.js originalmente intentaron seguir la especificación de CommonJS, pero luego cambiaron de decisión.

En lo que se refiere a módulos, la implementación en Node.js se vio influenciada:

// En circle.js
const PI = Math.PI;

exports.area = (r) => PI * r * r;
exports.circumference = (r) => 2 * PI * r;

// En otro archivo
const circle = require('./circle.js');
console.log('El área de 1 círculo de radio 4 es: ' + circle.area(4)); Existen abstracciones sobre el sistema de módulos de Node.js, en forma de bibliotecas, que actúan como un puente entre los módulos de Node.js y CommonJS. En este artículo solo vemos las características básicas.

Tanto en Node como en CommonJS, existen 2 palabras esenciales para interactuar con los módulos: require y exports.

require es una función que se puede usar para importar símbolos desde otro módulo al ámbito actual. El parámetro pasado a require es el id del módulo. En la implementación de Node, es el nombre del módulo dentro de la carpeta node_modules (o, en todo caso, la ruta hacia su ubicación).

exports es un objeto especial: todo lo que es puesto en él se puede exportar como un elemento público (conservando el nombre de los elementos).
Los módulos en CommonJS fueron diseñados teniendo en mente el desarrollo de lado del servidor. De forma natural, la API es síncrona. Es decir, los módulos son cargados en el momento y en el orden que se requieren dentro de un archivo de código fuente.

PROS

Es simple. Un desarrollador puede comprender el concepto sin ver la documentación.
Permite la gestión de dependencias: Los módulos requieren otros módulos, y se cargan con el orden solicitado.
require puede ser usado en todo lugar: los módulos se pueden cargar mediante programación.
Soporta dependencias circulares.

CONTRAS

Su API síncrona hace que su uso no sea adecuado para ciertos casos (lado del cliente).
Un archivo por módulo.
Los navegadores requieren una biblioteca para interpretarlo.
No hay 1 función constructora para los módulos (aunque Node lo admite).
Implementaciones

Ya hemos hablado de una implementación parcial: Node.js

Para el lado del cliente hay 2 opciones populares: webpack y browserify.

Asynchronous Module Definition (AMD)

AMD nació de un grupo de desarrolladores que estaban descontentos con la dirección adoptada por CommonJS. La principal diferencia entre AMD y CommonJS radica en su soporte para la carga asíncrona de módulos.

// Llamamos a define y le pasamos 1 arreglo de dependencias y 1 función que fabrica al módulo
define(['dependencia1', 'dependencia2'], function (dep1, dep2) {

    // Devolvemos una definición del módulo
    return function () {};
});

// Equivalente a:
define(function (require) {
    var dependencia1 = require('dependencia1'),
        dependencia2 = require('dependencia2');

    return function () {};
});

La carga asíncrona en JS es posible usando closures: una función es llamada cuando los módulos requeridos terminan de cargar.

La definición e importación de módulos se lleva a cabo por la misma función: cuando se define un módulo se indican sus dependencias de forma explícita.

De esta forma, un cargador AMD puede tener una imagen completa del gráfico de dependencias para un proyecto determinado en tiempo de ejecución.

Las bibliotecas que no dependen de otras pueden ser cargadas al mismo tiempo. Esto es muy importante para los navegadores, donde el tiempo de carga inicial es un punto esencial para brindar una buena experiencia de usuario.

PROS

Carga asíncrona (mejores tiempos de inicio).
Soporta dependencias circulares.
Es compatible con require y exports.
Gestión de dependencias totalmente integrada.
Los módulos se pueden separar en múltiples archivos si es necesario.
Soporta funciones constructoras.
Soporta plugins (para personalizar los pasos de carga).

CONTRAS

Sintácticamente es un poco más complejo.
Requiere de bibliotecas de carga, o bien de un proceso de transpilación.
Implementaciones

Las implementaciones más conocidas de AMD son require.js y Dojo.

Usar require.js es relativamente sencillo. Basta con incluir la biblioteca en nuestro HTML y usar el atributo data-main para indicar qué módulo debe cargarse primero. Dojo tiene una configuración similar.

Te esperamos en los siguientes artículos en donde hablaremos mas acerca de estos temas, los cuales hoy en día son de vital importancia en el mundo de la tecnología.

¿Te gustaría Javascript desde cero?
Tenemos los diplomados que necesitas.¡Haz clic aquí!
About Author

NGuerrero

0 0 votos
Article Rating
Suscribir
Notificar de
guest
0 Comments
Comentarios.
Ver todos los comentarios
0
¿Te gusta este articulo? por favor comentax
()
x
Abrir chat
¿Quieres aprender a programar?
Hola 👋,
¿Te interesa información de nuestros cursos?