Diferencia entre "module.exports" y "exports" en el sistema de módulos CommonJs

Resuelto Devs love ZenUML asked hace 11 años • 9 respuestas

En esta página ( http://docs.nodejitsu.com/articles/getting-started/what-is-require ), indica que "Si desea configurar el objeto de exportaciones en una función o un nuevo objeto, debe utilice el objeto module.exports."

Mi pregunta es por qué.

// right
module.exports = function () {
  console.log("hello world")
}
// wrong
exports = function () {
  console.log("hello world")
}

En la consola registré el resultado ( result=require(example.js)) y el primero es [Function]el segundo {}.

¿Podrías explicar la razón detrás de esto? Leí la publicación aquí: module.exports vs exports en Node.js. Es útil, pero no explica por qué está diseñado de esa manera. ¿Habrá algún problema si se devuelve directamente la referencia de exportaciones?

Devs love ZenUML avatar May 05 '13 17:05 Devs love ZenUML
Aceptado

modulees un objeto JavaScript simple con una exportspropiedad. exportses una variable de JavaScript simple que está configurada en module.exports. Al final de su archivo, node.js básicamente "volverá" module.exportsa la requirefunción. Una forma simplificada de ver un archivo JS en Node podría ser la siguiente:

var module = { exports: {} };
var exports = module.exports;

// your code

return module.exports;

Si establece una propiedad en exports, como exports.a = 9;, eso también se establecerá module.exports.aporque los objetos se pasan como referencias en JavaScript, lo que significa que si establece varias variables para el mismo objeto, todas son el mismo objeto; entonces exportsy module.exportsson el mismo objeto.
Pero si configura exportsalgo nuevo, ya no se configurará como module.exports, por lo exportsque ya module.exportsno son el mismo objeto.

goto-bus-stop avatar May 05 '2013 11:05 goto-bus-stop

La respuesta de Renee está bien explicada. Adición a la respuesta con un ejemplo:

Node hace muchas cosas con su archivo y una de las más importantes es ENVOLVER su archivo. Dentro de nodejs se devuelve el código fuente "module.exports". Demos un paso atrás y comprendamos el envoltorio. Supongamos que tienes

saludar.js

var greet = function () {
   console.log('Hello World');
};

module.exports = greet;

El código anterior está empaquetado como IIFE (Expresión de función invocada inmediatamente) dentro del código fuente de Nodejs de la siguiente manera:

(function (exports, require, module, __filename, __dirname) { //add by node

      var greet = function () {
         console.log('Hello World');
      };

      module.exports = greet;

}).apply();                                                  //add by node

return module.exports;                                      //add by node

y se invoca la función anterior (.apply()) y se devuelve module.exports. En este momento module.exports y exports apuntan a la misma referencia.

Ahora, imagina que reescribes greet.js como

exports = function () {
   console.log('Hello World');
};
console.log(exports);
console.log(module.exports);

la salida será

[Function]
{}

el motivo es: module.exports es un objeto vacío. No configuramos nada en module.exports, sino que configuramos exports = function()..... en el nuevo greet.js. Entonces, module.exports está vacío.

Técnicamente, las exportaciones y las exportaciones del módulo deberían apuntar a la misma referencia (¡eso es correcto!). Pero usamos "=" al asignar la función().... a las exportaciones, lo que crea otro objeto en la memoria. Entonces, module.exports y exports producen resultados diferentes. Cuando se trata de exportaciones, no podemos anularlo.

Ahora, imagina que reescribes (esto se llama Mutación) greet.js (refiriéndose a la respuesta de Renee) como

exports.a = function() {
    console.log("Hello");
}

console.log(exports);
console.log(module.exports);

la salida será

{ a: [Function] }
{ a: [Function] }

Como puede ver, module.exports y exports apuntan a la misma referencia que es una función. Si establece una propiedad en las exportaciones, se establecerá en module.exports porque en JS, los objetos se pasan por referencia.

La conclusión es utilizar siempre module.exports para evitar confusiones. Espero que esto ayude. Feliz codificación :)

Sdembla avatar Jan 06 '2017 02:01 Sdembla

Además, una cosa que puede ayudar a comprender:

matemáticas.js

this.add = function (a, b) {
    return a + b;
};

cliente.js

var math = require('./math');
console.log(math.add(2,2); // 4;

Genial, en este caso:

console.log(this === module.exports); // true
console.log(this === exports); // true
console.log(module.exports === exports); // true

Por lo tanto, de forma predeterminada, "esto" en realidad es igual a module.exports.

Sin embargo, si cambia su implementación a:

matemáticas.js

var add = function (a, b) {
    return a + b;
};

module.exports = {
    add: add
};

En este caso, funcionará bien, sin embargo, "esto" ya no es igual a module.exports porque se creó un nuevo objeto.

console.log(this === module.exports); // false
console.log(this === exports); // true
console.log(module.exports === exports); // false

Y ahora, lo que el require devolverá es lo que se definió dentro del module.exports, ya no esto ni exports.

Otra forma de hacerlo sería:

matemáticas.js

module.exports.add = function (a, b) {
    return a + b;
};

O:

matemáticas.js

exports.add = function (a, b) {
    return a + b;
};
Rodrigo Branas avatar Mar 25 '2016 13:03 Rodrigo Branas

La respuesta de René sobre la relación entre exportsy module.exportses bastante clara, se trata de referencias de JavaScript. Sólo quiero agregar eso:

Vemos esto en muchos módulos de nodos:

var app = exports = module.exports = {};

Esto asegurará que incluso si cambiamos module.exports, aún podamos usar las exportaciones haciendo que esas dos variables apunten al mismo objeto.

DevGrowth.Tech avatar Oct 02 '2014 15:10 DevGrowth.Tech