Diferencia entre "module.exports" y "exports" en el sistema de módulos CommonJs
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?
module
es un objeto JavaScript simple con una exports
propiedad. exports
es una variable de JavaScript simple que está configurada en module.exports
. Al final de su archivo, node.js básicamente "volverá" module.exports
a la require
funció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.a
porque 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 exports
y module.exports
son el mismo objeto.
Pero si configura exports
algo nuevo, ya no se configurará como module.exports
, por lo exports
que ya module.exports
no son el mismo objeto.
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 :)
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;
};
La respuesta de René sobre la relación entre exports
y module.exports
es 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.