javascript - module.exports vs exports no Node.js
Encontrei o seguinte contrato em um módulo Node.js:
module.exports = exports = nano = function database_module(cfg) {...}
Eu me pergunto qual é a diferença entre module.exports e exports e por que ambos são usados aqui.
Resposta #1
Embora a pergunta tenha sido respondida e aceita há muito tempo, eu só quero compartilhar meus 2 centavos.
Você pode imaginar que, no início do seu arquivo, há algo como (apenas para explicação):
var module = new Module(...);
var exports = module.exports;
Então, seja o que for que você faça, apenas lembre-se de que module.exports e NÃO exports será retornado do seu módulo quando você requerer esse módulo de outro lugar.
Então, quando você faz algo como:
exports.a = function() {
console.log("a");
}
exports.b = function() {
console.log("b");
}
Você está adicionando 2 funções a e b ao objeto para o qual module.exports aponta, então o typeof do resultado retornado será um object : { a: [Function], b: [Function] }
Claro, este é o mesmo resultado que você obterá se estiver usando module.exports neste exemplo em vez de exports.
Este é o caso em que você quer que seu module.exports se comporte como um contêiner de valores exportados. Por outro lado, se você só quer exportar uma função construtora, há algo que você deve saber sobre o uso de module.exports ou exports. Lembre-se de que module.exports será retornado quando você requerer algo, não exports.
module.exports = function Something() {
console.log('bla bla');
}
Agora o resultado retornado por typeof é 'function' e você pode requerê-lo e invocá-lo imediatamente assim:
var x=require('./file1.js')(); porque você sobrescreve o resultado retornado para ser uma função.
No entanto, usando exports você não pode usar algo como:
exports = function Something() {
console.log('bla bla');
}
var x = require('./file1.js')(); //Error: require is not a function
Porque com exports, a referência não aponta mais para o objeto onde module.exports aponta, então não há mais uma relação entre exports e module.exports. Neste caso, module.exports ainda aponta para o objeto vazio {} que será retornado.
A resposta aceita de outro tópico também deve ajudar: O JavaScript passa por referência?
Resposta #2
Configurar module.exports permite que a função database_module seja chamada como uma função quando required. Simplesmente configurar exports não permitiria que a função fosse exportada porque o Node exporta o objeto que module.exports referencia. O código a seguir não permitiria que o usuário chamasse a função.
module.js
O seguinte não funcionará.
exports = nano = function database_module(cfg) {return;}
O seguinte funcionará se module.exports estiver definido.
module.exports = exports = nano = function database_module(cfg) {return;}
console
var func = require('./module.js');
// the following line will **work** with module.exports
func();
Basicamente, node.js não exporta o objeto que exports atualmente referencia, mas exporta as propriedades do que exports originalmente referencia. Embora Node.js exporte o objeto que module.exports referencia, permitindo que você o chame como uma função.
2ª razão menos importante
Eles definem tanto module.exports quanto exports para garantir que exports não esteja referenciando o objeto exportado anteriormente. Ao definir ambos, você usa exports como um atalho e evita possíveis bugs mais adiante.
Usar exports.prop=true em vez de module.exports.prop=true economiza caracteres e evita confusão.
Resposta #3
Basicamente, a resposta está no que realmente acontece quando um módulo é requisitado via declaração require. Supondo que esta seja a primeira vez que o módulo está sendo requisitado.
Por exemplo:
var x = require('file1.js');
conteúdo de file1.js:
module.exports = '123';
Quando a declaração acima é executada, um objeto Module é criado. Sua função construtora é:
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 você pode ver, cada objeto de módulo tem uma propriedade com o nome exports. Isso é o que eventualmente é retornado como parte de require.
O próximo passo do require é envolver o conteúdo de file1.js em uma função anônima como abaixo:
(function (exports, require, module, __filename, __dirname) {
//contents from file1.js
module.exports = '123;
});
E esta função anônima é invocada da seguinte maneira, module aqui se refere ao Objeto Module criado 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 da função, o argumento formal exports se refere a module.exports. Essencialmente, é uma conveniência fornecida ao programador do módulo.
No entanto, essa conveniência precisa ser usada com cuidado. Em qualquer caso, se tentarmos atribuir um novo objeto a exports, devemos garantir que o façamos desta forma.
exports = module.exports = {};
Se fizermos isso da seguinte maneira maneira errada, module.exports ainda estará apontando para o objeto criado como parte da instância do módulo.
exports = {};
Como resultado, adicionar qualquer coisa ao objeto exports acima não terá efeito sobre o objeto module.exports e nada será exportado ou retornado como parte do require.
Resposta #4
Inicialmente, module.exports=exports, e a função require retorna o objeto ao qual module.exports se refere.
se nós adicionarmos uma propriedade ao objeto, digamos exports.a=1, então module.exports e exports ainda se referem ao mesmo objeto. Então, se chamarmos require e atribuirmos o módulo a uma variável, então a variável tem uma propriedade a e seu valor é 1;
Mas se nós sobrescrevermos um deles, por exemplo, exports=function(){}, então eles são diferentes agora: exports se refere a um novo objeto e module.exports se refere ao objeto original. E se nós requerermos o arquivo, ele não retornará o novo objeto, já que module.exports não se refere ao novo objeto.
Para mim, eu continuarei adicionando novas propriedades, ou substituindo ambas por um novo objeto. Apenas substituir uma não está certo. E tenha em mente que module.exports é o verdadeiro chefe.
Resposta #5
exports e module.exports são os mesmos, a menos que você reatribua exports dentro do seu módulo.
A maneira mais fácil de pensar sobre isso é imaginar que esta linha está implicitamente no topo de cada módulo.
var exports = module.exports = {};
Se, dentro do seu módulo, você reassinar exports, então você o reassina dentro do seu módulo e ele não será mais igual a module.exports. É por isso que, se você quiser exportar uma função, você deve fazer:
module.exports = function() { ... }
Se você simplesmente atribuísse sua function() { ... } ao exports, você estaria reatribuindo o exports para não apontar mais para module.exports.
Se você não quiser se referir à sua função por module.exports toda vez, você pode fazer:
module.exports = exports = function() { ... }
Observe que module.exports é o argumento mais à esquerda.
Anexar propriedades ao exports não é a mesma coisa, pois você não está reatribuindo-o. É por isso que isso funciona
exports.foo = function() { ... }
Resposta #6
O JavaScript passa objetos por cópia de referência
É uma diferença sutil relacionada à forma como os objetos são passados por referência no JavaScript.
exports e module.exports apontam para o mesmo objeto. exports é uma variável e module.exports é um atributo do objeto módulo.
Diga que eu escreva algo assim:
exports = {a:1};
module.exports = {b:12};
exports e module.exports agora apontam para objetos diferentes. Modificar exports não modifica mais module.exports.
Quando a função de importação inspeciona module.exports, ela obtém {b:12}
Resposta #7
Eu só fiz alguns testes, e aconteceu que, dentro do código do módulo do Node.js, deveria ser algo assim:
var module.exports = {};
var exports = module.exports;
então :
Um
exports = function(){}; // this will not work! as it make the exports to some other pointer
module.exports = function(){}; // it works! cause finally nodejs make the module.exports to export.
Dois
exports.abc = function(){}; // works!
exports.efg = function(){}; // works!
3: mas, enquanto neste caso
module.exports = function(){}; // from now on we have to using module.exports to attach more stuff to exports.
module.exports.a = 'value a'; // works
exports.b = 'value b'; // the b will nerver be seen cause of the first line of code we have do it before (or later)
Resposta #8
Aqui está uma boa descrição escrita sobre módulos do Node no livro Node.js in Action da publicação Manning.
O que é exportado em sua aplicação, em última análise, é module.exports.
exports é configurado simplesmente como uma referência global para module.exports, que inicialmente é definido como um objeto vazio ao qual você pode adicionar propriedades. Portanto, exports.myFunc é apenas uma abreviação para module.exports.myFunc.
Como resultado, se exports for definido como qualquer outra coisa, ele quebra a referência entre module.exports e exports. Porque module.exports é o que realmente é exportado, exports não funcionará mais como esperado — ele não referencia mais module.exports. Se você quiser manter esse vínculo, você pode fazer com que module.exports referencie exports novamente da seguinte maneira:
module.exports = exports = db;
Resposta #9
Para entender as diferenças, você precisa primeiro entender o que o Node.js faz com cada módulo durante o tempo de execução. O Node.js cria uma função wrapper para cada módulo:
(function(exports, require, module, __filename, __dirname) {
})()
Observe que o primeiro parâmetro exports é um objeto vazio, e o terceiro parâmetro module é um objeto com muitas propriedades, e uma das propriedades é chamada exports. É de onde vem exports e de onde vem module.exports. O primeiro é um objeto variável, e o último é uma propriedade do objeto module.
Dentro do módulo, o Node.js faz automaticamente esta coisa no início: module.exports=exports, e finalmente retorna module.exports.
Então você pode ver que se você reatribuir algum valor a exports, isso não terá nenhum efeito em module.exports. (Simplesmente porque exports aponta para outro objeto novo, mas module.exports ainda mantém o velho exports)
let exports = {};
const module = {};
module.exports = exports;
exports = { a: 1 }
console.log(module.exports) // {}
Mas se você atualizar as propriedades de exports, isso certamente terá efeito em module.exports. Porque ambos apontam para o mesmo objeto.
let exports = {};
const module = {};
module.exports = exports;
exports.a = 1;
module.exports.b = 2;
console.log(module.exports) // { a: 1, b: 2 }
Observe também que se você reatribuir outro valor para module.exports, então parecerá sem sentido atualizar exports. Todas as atualizações em exports são ignoradas porque module.exports aponta para outro objeto.
let exports = {};
const module = {};
module.exports = exports;
exports.a = 1;
module.exports = {
hello: () => console.log('hello')
}
console.log(module.exports) // { hello: () => console.log('hello')}
