¿Qué es un módulo en JS?

¿Alguna vez has escuchado el término Módulo al momento de trabajar en un proyecto? Es muy probable que si. La palabra es comúnmente usada en cualquier proyecto para referirse al concepto de una sección o una parte de una aplicación. Pero en JS (y de hecho en la mayoría de los lenguajes) existe una definición más técnica de la palabra.

Pero tu estás aquí por un módulo en Javascript y si no…pues no se porque estás aquí, pero bienvenido igualmente.

Un módulo es una colección de datos y funciones relacionadas entre sí y que además contiene elementos con accesibilidad pública y privada. Además es stateful (con estado), es decir, mantiene los cambios hecho en la información y brinda funcionalidad para acceder y actualizar dichos datos.

Estoy seguro que entendiste todo eso a la primera y que no tuviste que reelerlo, pero a pesar de eso creo que como todo en la vida. A veces no es suficiente con una explicación teórica.

Lo que no es

A veces para entender que es un concepto es mas facil empezar por lo que no es…

Namespaces (stateless)

Un namespace es uno de esos conceptos en programación que ya haz utilizado un sin fin de veces pero nunca te enteraste de que si quiera tuviera un nombre.

Es muy simple. Imagina que tienes un objeto vacio:

const theGreatestUtils = {}

Y ahora crea unas funciones dentro del mismo:

const theGreatestUtils = {
    addOne(value) {
        return value + 1
    },
    minusTwo(value) {
        return value - 2
    },
    multiplyByFive(value) {
        return value * 5
    },
    divideByTen(value) {
        return value / 10
    }
}

¡Listo! Ya tienes un namespace…

Si así de sin chiste es. Recuerda, si encapsulas a tus funciones dentro de un objeto puedes referirte a ese objeto como un namespace.

En este caso se puede decir que las 4 funciones dentro del objeto theGreatestUtils están dentro del namespace theGreatestUtils.

Se dice que esto es una estructura stateless (sin estado), que no es más que una manera de decir que no se guarda el estado de la información. Esto es evidente ya que como puedes ver no hay ningun valor almacenado dentro del objeto theGreatestUtils y por ende ningún estado que mantener.

Si recuerdas dentro de la breve y facilísima de recordar definición de un módulo menciona que este deber ser stateful. Obviamente entonces esto no es un módulo.


Namespaces (stateful)

¿Pero qué nos evita tener un namespace stateful? Nada. Sólo hay que agregarle datos de los que guardar un estado.

const theBestUtils = {

    list: [],
    addToList(item) {
        this.list.push(item)
    },
    deleteLastElementFromList() {
        this.list.pop()
    }
}

Si te fijas al llamar addToList se modifica el elemento list permanentemente. Lo mismo ocurre si llamas a deleteLastElementFromList.

¿Entonces ya tenemos un módulo?


Lo que sí es

Ya veías esto venir. La pregunta es solo para efecto dramático.

La respuesta es no. A pesar de que la estructura de datos anterior es stateful aún le hace falta algo importante: elementos con accessibildad pública y privada. ¿Cómo podemos incorporar estos niveles de accesibilidad? La respúesta es simple.

const transactions = function() {
    const transactionsList = [
        {   
            id: 1,
            amount: 54.56
        },
        {   
            id: 2,
            amount: 119.00
        }
    ]

    function getTransactionData(transactionId) {
        return transactionsList.find(transaction => transaction.id === transactionId)
    }

    return {
        getTransactionData
    }
    
}

const transactionInstance = transactions()

const transactionItem = transactionInstance.getTransactionData(2)

console.dir(transactionItem)

const list = transactionInstance.transactionsList
console.dir(list)

¿Qué es lo que está pasando en este código?

Veamos elemento por elemento.

  • transactions – es un objeto que contiene la definción del módulo. El módulo es una función stateful que a su vez contiene otros elementos.
    • transactionsList – es una lista que contiene datos de transacciones
    • getTransactionData – es una función interna que busca una transacción en transactionsList básandose en el parámetro transactionId que se le pase.
    • return – Más que explicarte lo que hace el return, el punto importante aquí es ver que es lo que se regresa. Un objeto con la función interna como elemento. (¿Por qué se hace esto? En un momento lo veremos.)
  • transactionsInstance – es un objeto que contiene una instancia del módulo. Desde la instancia podemos acceder a los elementos públicos del módulo.
  • transactionItem – contiene el resultado de buscar un elemento en transactionList con id 2.

Finalmente console.dir imprime el contenido del objeto transactionItem.

{ id: 2, amount: 119 }

Para este punto probablemente ya tienes tus sospechas pero los valores que regresamos en el return están directamente relacionados con la accesibilidad de los elementos.

Es fácil.

Lo que regresas en el return es público y lo que no regresas en el return es privado.

Para comprobarlo puedes utilizar transactionsInstance. En el ejemplo pudiste acceder sin problemas a la función getTransactionData. Sin embargo si intentas acceder al objeto transactionsList

const list = transactionInstance.transactionsList
console.dir(list)
undefined

Al ser un elemento privado no encontrarás el arreglo de transactionsList definido.

Pero digamos que quieres poder acceder al elemento. ¿Qué haces? Pues solo tienes quieres hacerlo público. Y si quieres hacerlo público solo tienes que agregarlo en el return del módulo.

return {
        transactionsList,
        getTransactionData
}

Ahora intenta correr el código de nuevo.

const list = transactionInstance.transactionsList
console.dir(list)
[
  { id: 1, amount: 54.56 },
  { id: 2, amount: 119 }
]

Lo que esperabamos. Ahora si se puede acceder al arreglo.

¿Simple no?

Claro, realmente el punto de hacer privado el arreglo de transactionsList es que precisamente no pueda ser modificado.

Esto es algo muy común si estas obteniendo la información de una base de datos. En casos asi no hay razón para que permitas que se pueda acceder a los datos directamente. Lo único que debería hacer tu código es hacer las llamadas correctas a la base de datos, garantizando que la información no ha sido modificada después de la llamada.

A su vez también puede ser útil como una manera de indicarle a otros desarrolladores que clase de elementos se espera que no se modifiquen ciertos datos.

Ahora ya terminaste de entender que es un módulo clásico de JS. ¿Pero qué crees? Aún hay más. JS ofrece algunas variaciones de módulos y obvio es importante que las conozcas.