module.exports vs exportaciones en Node.js
Encontré el siguiente contrato en un módulo de Node.js:
module.exports = exports = nano = function database_module(cfg) {...}
Me pregunto cuál es la diferencia entre module.exports
y exports
por qué se usan ambos aquí.
Aunque la pregunta fue respondida y aceptada hace mucho tiempo, solo quiero compartir mis 2 centavos.
Puedes imaginar que al principio de tu archivo hay algo como (solo como explicación):
var module = new Module(...);
var exports = module.exports;
Entonces, hagas lo que hagas, ten en cuenta que module.exports
y NO exports
se devolverá de tu módulo cuando requieras ese módulo desde otro lugar.
Entonces, cuando haces algo como:
exports.a = function() {
console.log("a");
}
exports.b = function() {
console.log("b");
}
Está agregando 2 funciones a
y b
al objeto al que module.exports
apunta, por lo que el typeof
resultado devuelto será object
:{ a: [Function], b: [Function] }
Por supuesto, este es el mismo resultado que obtendrá si utiliza module.exports
en este ejemplo en lugar de exports
.
Este es el caso en el que desea module.exports
que se comporte como un contenedor de valores exportados. Mientras que, si solo desea exportar una función constructora, entonces hay algo que debe saber sobre el uso module.exports
de o exports
. Recuerde que module.exports
se le devolverá cuando requiera algo, no exports
.
module.exports = function Something() {
console.log('bla bla');
}
Ahora typeof
el resultado devuelto es 'function'
y puede solicitarlo e invocarlo inmediatamente como:
var x = require('./file1.js')();
porque sobrescribe el resultado devuelto para que sea una función.
Sin embargo, al usar exports
no puedes usar algo como:
exports = function Something() {
console.log('bla bla');
}
var x = require('./file1.js')(); //Error: require is not a function
Porque con exports
, la referencia ya no apunta al objeto al que module.exports
apunta, por lo que ya no hay relación entre exports
y module.exports
. En este caso module.exports
todavía apunta al objeto vacío {}
que será devuelto.
La respuesta aceptada de otro tema también debería ayudar: ¿ JavaScript pasa por referencia?
La configuración module.exports
permite database_module
llamar a la función como una función cuando required
. La simple configuración exports
no permitiría exportar la función porque el nodo exporta las module.exports
referencias del objeto. El siguiente código no permitiría al usuario llamar a la función.
módulo.js
Lo siguiente no funcionará.
exports = nano = function database_module(cfg) {return;}
Lo siguiente funcionará si module.exports
está configurado.
module.exports = exports = nano = function database_module(cfg) {return;}
consola
var func = require('./module.js');
// the following line will **work** with module.exports
func();
Básicamente, node.js no exporta el objeto al que exports
hace referencia actualmente, sino que exporta las propiedades de lo que exports
hace referencia originalmente. Aunque Node.js exporta las module.exports
referencias de objetos, lo que le permite llamarlo como una función.
2da razón menos importante
Configuran ambos module.exports
y exports
para asegurarse de que exports
no haga referencia al objeto exportado anterior. Al configurar ambos, los utiliza exports
como taquigrafía y evita posibles errores más adelante.
Usar exports.prop = true
en lugar de module.exports.prop = true
guarda caracteres y evita confusiones.
Básicamente la respuesta está en lo que realmente sucede cuando se requiere un módulo mediante require
una declaración. Suponiendo que esta es la primera vez que se requiere el módulo.
Por ejemplo:
var x = require('file1.js');
Contenido del archivo 1.js:
module.exports = '123';
Cuando se ejecuta la declaración anterior, Module
se crea un objeto. Su función constructora es:
function Module(id, parent) {
this.id = id;
this.exports = {};
this.parent = parent;
if (parent && parent.children) {
parent.children.push(this);
}
this.filename = null;
this.loaded = false;
this.children = [];
}
Como puede ver, cada objeto de módulo tiene una propiedad con nombre exports
. Esto es lo que finalmente se devuelve como parte de require
.
El siguiente paso de require es empaquetar el contenido de file1.js en una función anónima como se muestra a continuación:
(function (exports, require, module, __filename, __dirname) {
//contents from file1.js
module.exports = '123;
});
Y esta función anónima se invoca de la siguiente manera, module
aquí se refiere al Module
Objeto creado anteriormente.
(function (exports, require, module, __filename, __dirname) {
//contents from file1.js
module.exports = '123;
}) (module.exports,require, module, "path_to_file1.js","directory of the file1.js");
Como podemos ver dentro de la función, exports
el argumento formal se refiere a module.exports
. En esencia, es una comodidad proporcionada al programador del módulo.
Sin embargo, esta comodidad debe ejercerse con cuidado. En cualquier caso, si intentamos asignar un nuevo objeto a las exportaciones, asegúrese de hacerlo de esta manera.
exports = module.exports = {};
Si lo hacemos de forma incorrecta , module.exports
seguirá apuntando al objeto creado como parte de la instancia del módulo.
exports = {};
Como resultado, agregar algo al objeto de exportación anterior no tendrá ningún efecto en el objeto module.exports y no se exportará ni devolverá nada como parte del requisito.
Inicialmente, module.exports=exports
y la require
función devuelve el objeto module.exports
al que se refiere.
si agregamos propiedad al objeto, digamos exports.a=1
, entonces module.exports y exports aún se refieren al mismo objeto. Entonces, si llamamos a require y asignamos el módulo a una variable, entonces la variable tiene una propiedad a y su valor es 1;
Pero si anulamos uno de ellos, por ejemplo, exports=function(){}
, ahora son diferentes : exports se refiere a un nuevo objeto y module.exports se refiere al objeto original. Y si requerimos el archivo, no devolverá el nuevo objeto, ya que module.exports no se refiere al nuevo objeto.
Para mí, seguiré agregando nuevas propiedades o anularé ambas a un nuevo objeto. Simplemente anular uno no está bien. Y tenga en cuenta que ese module.exports
es el verdadero jefe.
exports
y module.exports
son los mismos a menos que los reasigne exports
dentro de su módulo.
La forma más sencilla de pensarlo es pensar que esta línea está implícitamente en la parte superior de cada módulo.
var exports = module.exports = {};
Si, dentro de tu módulo, reasignas exports
, entonces lo reasignas dentro de tu módulo y ya no es igual a module.exports
. Es por esto que, si deseas exportar una función, debes hacer:
module.exports = function() { ... }
Si simplemente asignara su function() { ... }
a exports
, lo reasignaría exports
para ya no señalar module.exports
.
Si no desea hacer referencia a su función cada module.exports
vez, puede hacer:
module.exports = exports = function() { ... }
Observe que module.exports
es el argumento más a la izquierda.
Adjuntar propiedades exports
no es lo mismo ya que no lo está reasignando. Por eso esto funciona
exports.foo = function() { ... }