Lección 15 · 15 de 20
Patrón módulo e IIFE · JavaScript
Oculte el estado interno en JavaScript con IIFE y el patrón módulo, y vea cómo Node envuelve cada fichero y qué exporta module.exports.

Una IIFE (expresión de función invocada inmediatamente) es una función que se define y se ejecuta en el mismo momento: (function () { ... })(). Los paréntesis exteriores la convierten en una expresión y los últimos la llaman. Todo lo que se declara dentro, como items y log, queda en su ámbito privado.
El patrón módulo combina la IIFE con las clausuras: devuelve un objeto solo con lo que quiere hacer público (add y count), que siguen teniendo acceso a items. Desde fuera, inventory.items es undefined: el inventario solo se puede cambiar a través de su interfaz. Antes de que existieran los módulos, así se organizaba el JavaScript de las páginas web.
Node hace algo muy parecido con cada fichero CommonJS: lo envuelve en una función con cinco parámetros (exports, require, module, __filename y __dirname). Por eso arguments.length vale 5 en el nivel superior del fichero y sus variables no se mezclan con las de otros ficheros.
Lo que un fichero exporta es `module.exports`; exports es solo un atajo que al principio apunta al mismo objeto. Si asigna un objeto nuevo a module.exports, como hace el ejemplo, exports sigue apuntando al antiguo y deja de servir: use exports.name = ... para añadir o module.exports = ... para sustituir, nunca exports = .... Además, require() guarda cada módulo en caché, así que dos llamadas devuelven el mismo objeto.
Código · main.js
// IIFE: a function that runs at once and keeps its variables private.const inventory = (function () { const items = new Map(); // nothing outside can reach this const log = (msg) => console.log(`[inventory] ${msg}`); return { add(name, qty) { items.set(name, (items.get(name) ?? 0) + qty); log(`+${qty} ${name}`); }, count: (name) => items.get(name) ?? 0, };})();inventory.add("pens", 20);inventory.add("pens", 5);console.log("pens:", inventory.count("pens"), "| inventory.items:", inventory.items);// Node wraps every CommonJS file in a function just like that one:// (function (exports, require, module, __filename, __dirname) { ... })console.log("Wrapper arguments:", arguments.length, "|", typeof require, typeof module);console.log("exports === module.exports:", exports === module.exports);// Whatever ends up in module.exports is what require() returns.module.exports = { inventory, version: "1.2.0" };console.log("Exported:", Object.keys(module.exports).join(", "));console.log("exports still points to the old object:", exports !== module.exports);// require() caches modules: a second call returns the same object.const first = require("path");const second = require("path");console.log("Cached:", first === second, "| extname:", first.extname("orders.json"));Salida · node main.js
[inventory] +20 pens [inventory] +5 pens pens: 25 | inventory.items: undefined Wrapper arguments: 5 | function object exports === module.exports: true Exported: inventory, version exports still points to the old object: true Cached: true | extname: .json
Para recordar
- Una IIFE se ejecuta al definirse y crea un ámbito privado.
- El patrón módulo expone solo una interfaz y oculta el estado en una clausura.
- Node envuelve cada fichero CommonJS en una función.
- Lo exportado es
module.exports;exportses solo una referencia inicial. require()cachea los módulos.
Ejercicio
Añada al inventario un método remove(name, qty) que no permita dejar existencias negativas, y un contador privado de operaciones que se pueda consultar con un método operations().