Diferencia entre "módulo.exportaciones" y "exportaciones" en el sistema de módulos CommonJs

277

En esta página ( http://docs.nodejitsu.com/articles/getting-started/what-is-require ), establece que "si desea establecer el objeto de exportación en una función o un nuevo objeto, debe use el objeto module.exports ".

Mi pregunta es por qué.

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

Console.logged el resultado ( result=require(example.js)) y el primero es [Function]el segundo es {}.

¿Podría explicar la razón detrás de esto? He leído el post aquí: module.exports vs exportaciones en Node.js . Es útil, pero no explica la razón por la que está diseñado de esa manera. ¿Habrá algún problema si la referencia de las exportaciones se devuelve directamente?

Xiao Peng - ZenUML.com
fuente
11
Siempre uso module.exports.
Gabriel Llamas
1
Creo que seguir los consejos mencionados anteriormente permite evitar este problema.
Vitalii Korsakov
@GabrielLlamas, ¿por qué muchos paquetes usan solo exports, por ejemplo github.com/tj/consolidate.js/blob/master/lib/consolidate.js ?
CodyBugstein
3
@Imray Si utiliza siempre module.exports, que nunca será mal, pero se puede utilizar exportssi no está reemplazando el objeto predeterminado exportados, es decir, si simplemente adjuntar propiedades como esta: var foo = require('foo').foo. Esta foopropiedad se puede exportar así: exports.foo = ...y por supuesto también con module.exports. Es una elección personal, pero actualmente estoy usando module.exportsy exportsapropiada.
Gabriel Llamas
Prefiero exportaciones.myFunc = function () {}, así que no tengo que mantener una lista de exportaciones al final del archivo. Se siente más cercano a la práctica común de exportar cuando declaras en ES6.
SacWebDeveloper

Respuestas:

626

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

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 múltiples variables en el mismo objeto, todos son el mismo objeto; entonces entonces exportsy module.exportsson el mismo objeto.
Sin embargo, si se establece exportsa algo nuevo, que ya no se establece en module.exports, por lo que exports, y module.exportsya no son el mismo objeto.

parada de autobús
fuente
11
Correcto, son solo conceptos básicos de los tipos de referencia.
Vitalii Korsakov
18
¿¡Por qué!? Por qué uno puede leer esto solo aquí. Esto debería ser un eslogan para cada javaScript modular. Gracias
lima_fil
8
Bellamente explicado!
Aakash Verma
3
impresionante, la mejor respuesta !!
John
55
Gran explicación La documentación module.exportstambién lo describe: nodejs.org/api/modules.html#modules_module_exports
Brian Morearty
52

La respuesta de Renee está bien explicada. Además de la respuesta con un ejemplo:

Node hace muchas cosas a su archivo y una de las más importantes es WRAPPING su archivo. Dentro del código fuente de nodejs se devuelve "module.exports". Vamos a dar un paso atrás y entender el envoltorio. Supongamos que tienes

greet.js

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

module.exports = greet;

el código anterior se envuelve 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 la función anterior se invoca (.apply ()) y devuelve module.exports. En este momento modulo. Exportaciones y exportaciones apuntando 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]
{}

la razón es: module.exports es un objeto vacío. No establecimos nada en module.exports sino que establecimos exportaciones = función () ..... en new greet.js. Entonces, module.exports está vacío.

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

Ahora, imagine que reescribe (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 and export apunta a la misma referencia, que es una función. Si establece una propiedad en exportaciones, se establecerá en module.exports porque en JS, los objetos se pasan por referencia.

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

Sdembla
fuente
Esta también es una hermosa respuesta perspicaz y complementa la respuesta de @ goto-bus-stop. :)
varun
23

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

math.js

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

client.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, por defecto, "esto" es realmente igual a module.exports.

Sin embargo, si cambia su implementación a:

math.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 devolverá el requerimiento es lo que se definió dentro del módulo. Exportaciones, no esto o exportaciones, nunca más.

Otra forma de hacerlo sería:

math.js

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

O:

math.js

exports.add = function (a, b) {
    return a + b;
};
Rodrigo Branas
fuente
15

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

Vemos esto en muchos módulos de nodos:

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

Esto asegurará que incluso si cambiamos module.exports, todavía podemos usar exportaciones haciendo que esas dos variables apunten al mismo objeto.

fengshuo
fuente
Me confundí con esta explicación, ¿amable de elaborar?
GuyFreakz
66
@GuyFreakz No estoy seguro si esto habla a su confusión, pero module.exportsy exportsson variables independientes, simplemente inicializados para hacer referencia al mismo objeto. Si cambia la referencia de una variable, las dos variables ya no hacen referencia a la misma cosa. La línea de código anterior asegura que ambas variables se inicialicen en el mismo objeto nuevo.
Andrew Palmer
Un caso de uso real que todos los demás se perdieron en @fengshuo. ¡Gracias!
Aakash Verma
0

myTest.js

module.exports.get = function () {};

exports.put = function () {};

console.log(module.exports)
// output: { get: [Function], put: [Function] }

exportsy module.exportsson lo mismo y una referencia al mismo objeto. Puede agregar propiedades de ambas maneras según su conveniencia.

Shashwat Gupta
fuente