Skip to content

2 Modularización

Patrón de diseño módulo

El patrón de diseño módulo tiene como objetivo separar el código Javascript en diferentes módulos (a veces en diferentes ficheros), de tal modo que el código sea más legible, entendible, reusable y fácil de acceder.

Podemos entender como un módulo una función que se comporta como un contenedor para un contexto de ejecución. Dentro de está función se pueden declarar diferentes variables.

Lo primero que debemos hacer es crear una función global con el código que quiero reutilizar, por ejemplo variables y otros métodos.

function myApp() {
    var number = 0, letter = 'A'

    function apply(increment){
        return number + increment
    }
}

La función deberá devolver un objeto con el nombre de acceso a las variables y a los métodos que contiene, de esta manera se podrán acceder a ellos fuera de la misma función:

function myApp() {
    var number = 0, letter = 'A'

    function apply(increment){
        return number + increment
    }

    return {
        number: number,
        letter: letter,
        apply: apply
    }
}

Como se puede observar el objeto debe tener un nombre para poder acceder a él fuera de la función myApp. Si el nombre de la variable y/o método es el mismo se puede omitir escribiéndolo una única vez.

function myApp() {
    var number = 0, letter = 'A'

    function apply(increment){
        return number + increment
    }

    return { number, letter, apply }
}

De esta forma podemos crear variables y métodos que sean públicos o privados. Los públicos son los que retornamos e incluimos en el objeto y los privados son los que no retornamos.

function myApp() {
    var number = 0, letter = 'A'

    const sum = (a, b) => a + b // Función privada que no se retorna

    function apply(increment){
        return sun(number, increment)
    }

    return { number, letter, apply }
}

Además para poder acceder al módulo primero habrá que invocar la función:

function myApp() {
    var number = 0, letter = 'A'

    const sum = (a, b) => a + b // Función privada que no se retorna

    function apply(increment){
        return sun(number, increment)
    }

    return { number, letter, apply }
}

const variable = myApp()

console.log(variable.number) // 0
console.log(variable.letter) // A
console.log(variable.apply(10)) // 10

Esto se puede simplificar realizando la autoinvocación, es decir, creando la función y luego usando () para invocarla. Para ello la función tiene que estar entre paréntesis para que primero se cree y luego se invoque:

const variable = (function myApp() {
    var number = 0, letter = 'A'

    const sum = (a, b) => a + b // Función privada que no se retorna

    function apply(increment){
        return sun(number, increment)
    }

    return { number, letter, apply }
})()

console.log(variable.number) // 0
console.log(variable.letter) // A
console.log(variable.apply(10)) // 10

Se puede reducir más haciendo uso de las arrow functions con autoinvocación:

const myApp = (() => {
    var number = 0, letter = 'A'

    const sum = (a, b) => a + b // Función privada que no se retorna

    function apply(increment){
        return sun(number, increment)
    }

    return { number, letter, apply }
})()

console.log(myApp.number) // 0
console.log(myApp.letter) // A
console.log(myApp.apply(10)) // 10

Una de las ventajas que tiene la modularización es que se puede usar la destructuración para acceder a las variables y métodos internos del módulo:

const myApp = (() => {
    var number = 0, letter = 'A'

    const sum = (a, b) => a + b // Función privada que no se retorna

    function apply(increment){
        return sun(number, increment)
    }

    return { number, letter, apply }
})()

const {number, letter, apply} = myApp

console.log(number) // 0
console.log(letter) // A
console.log(apply(10)) // 10
Ejercicio 1

Realiza una aplicación con Javascript la cual realice las operaciones básicas: sumar, restar, multiplicar y dividir. Haciendo uso del patrón de módulos, pon a prueba las funciones creadas.

Importar y exportar

Para poder usar las variables y los métodos de un fichero Javascript en otro fichero en Node JS es necesario exportar el código en el fichero de la función que se desea reutilizar e importarlo en el fichero que se desea usar.

En Node JS existen dos maneras de realizar dichas operaciones, una más convencional y antigua pero que aún así sigue en uso actualmente, denominada CommonJS, y otra más moderna que hace uso del ECMAScript 6 (ES6). El problema de la segunda es que de forma nativa no se puede usar con archivos .js si no con archivos .mjs, aunque existe una forma de poder utilizarlo en Node JS que veremos más adelante.

CommonJS

Para poder exportar una función dentro de un fichero Javascript es necesario usar module.export:

const myApp = (() => {
    var number = 0, letter = 'A'

    const sum = (a, b) => a + b // Función privada que no se retorna

    function apply(increment){
        return sun(number, increment)
    }

    return { number, letter, apply }
})()

module.exports = myApp

Nótese, que la autoinvocación desaparece ya que se puede realizar posteriormente. Incluso se puede exportar de forma directa:

module.exports = () => {
    var number = 0, letter = 'A'

    const sum = (a, b) => a + b // Función privada que no se retorna

    function apply(increment){
        return sun(number, increment)
    }

    return { number, letter, apply }
}

Para importar el módulo se usa la función require cuyo parámetro es la ruta relativa en la que se encuentra el fichero:

const myApp = require('myApp')

const variable = myApp()

console.log(variable.numero) // 0

Se puede realizar la exportanción de más módulos haciendo indicando el nombre:

module.exports.moduleA = () => {
    var number = 0, letter = 'A'

    const sum = (a, b) => a + b // Función privada que no se retorna

    function apply(increment){
        return sun(number, increment)
    }

    return { number, letter, apply }
}

module.exports.moduleB = () => {
    return {
        sum: (a, b) => a + b,
        min: (a, b) => Maths.min(a, b)
    }
}
const moduleA = require('myApp.js').moduleA    
const moduleB = require('myApp.js').moduleB

moduleA()
moduleB()
const { moduleA } = require('myApp.js')    
const { moduleB } = require('myApp.js')

moduleA()
moduleB()

Hacer esto es lo mismo que unir ambos módulos en uno solo.

module.exports = () => {
    var number = 0, letter = 'A'

    const sum = (a, b) => a + b 
    const min = Maths.min(a, b)

    function apply(increment){
        return sum(number, increment)
    }

    return { 
        moduleA : { number, letter, apply },
        moduleB : { sum, min }
    }
}

En Node JS, cuando todas las funciones son públicas y demás se puede crear las funciones de forma independiente y luego exportar un único objeto con todas las funciones necesarias. La ventaja de usar este forma es que no será necesario invocar ninguna función en el lugar que se use los métodos o variables del módulo:

const sum = (a, b) => a + b
const div = (a, b) => a / b
const PI = 3.14

module.exports = { sum, div, pi: PI}
const { sum, pi } = require("myApp.js")

console.log(sum(1, 2)) // 3
console.log(pi) // 3.14 

Info

Con el uso de require no es obligatorio indicar la extensión del archivo a importar

Ejercicio 2

Realiza el mismo ejercicio anterior en fichero myApp, exportalos como creas conveniente haciendo uso del CommonJS y utilízalo dentro del fichero index

ECMAScript 6 (ES6)

En Node JS, en principio, para poder usar la importación y la exportación del ECMas Script 6 (ES6) será necesario usar la extensión del fichero .mjsen lugar de .js

Para poder exportar una función, o directamente un objeto como en el apartado anterior, se realizará con la palabra reservada export:

const sum = (a, b) => a + b

export { sum }

Para poder importarla sería necesario usar la palabra reservada import ... from ...:

import module from 'myApp.msj'

console.log(module.sum(1, 2)) // 3

En vez de usar el nombre del módulo, también se puede destructurar:

import { sum } from 'myApp.msj'

console.log(sum(1, 2)) // 3

Si se desea importar todos los métodos y variables del módulo haciendo uso de la destructuración se puede indicar *:

import * from 'myApp.msj'

console.log(sum(1, 2)) // 3

Warning

Ten en cuenta que en la importación del ES6 SI es obligatorio indicar la extensión del archivo. Esto supone un problema, porque tecnologías que usan Node JS como Webpack, React, etc., no hay que indicarlo ya que implícitamente se encargan de indicarlo.

También se puede mezclar en un mismo proyectos ficheros .js con la exportación CommonJS con ficheros .mjs con la exportación ES6, aunque no es recomendable.

Ejercicio 3

Realiza el mismo ejercicio anterior en fichero myApp, exportalos como creas conveniente haciendo uso del ES6 y utilízalo dentro del fichero index