Diferença entre "module.exports" e "exports" no CommonJs Module System

277

Nesta página ( http://docs.nodejitsu.com/articles/getting-started/what-is-require ), afirma que "Se você deseja definir o objeto de exportação para uma função ou um novo objeto, é necessário use o objeto module.exports. "

Minha pergunta é o porquê.

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

Eu console.logging o resultado ( result=require(example.js)) e o primeiro é [Function]o segundo é {}.

Poderia explicar o motivo por trás disso? Eu li o post aqui: module.exports vs exportações em Node.js . É útil, mas não explica o motivo pelo qual foi projetado dessa maneira. Haverá um problema se a referência das exportações for retornada diretamente?

Xiao Peng - ZenUML.com
fonte
11
Sempre use module.exports.
Gabriel Llamas
1
Eu acho que seguir os conselhos mencionados acima permite evitar esse problema.
Vitalii Korsakov 26/09
@GabrielLlamas, então por que muitos pacotes usam apenas exports, por exemplo, github.com/tj/consolidate.js/blob/master/lib/consolidate.js ?
CodyBugstein
3
@Imray Se você sempre usa module.exports, você nunca vai estar errado, mas você pode usar exportsse você não está substituindo o padrão exportados objeto, isto é, se você simplesmente anexar propriedades como esta: var foo = require('foo').foo. Esta foopropriedade pode ser exportada assim: exports.foo = ...e, claro, também com module.exports. É uma escolha pessoal, mas atualmente estou usando module.exportse de forma exportsadequada.
Gabriel Llamas
Eu prefiro export.myFunc = function () {}, portanto não preciso manter uma lista de exportações na parte inferior do arquivo. Parece mais prática comum de exportação quando você declara no ES6.
7302 SacWebDeveloper

Respostas:

625

moduleé um objeto JavaScript simples com uma exportspropriedade exportsé uma variável JavaScript simples que passa a ser definido como module.exports. No final do seu arquivo, o node.js basicamente 'retornará' module.exportsà requirefunção. Uma maneira simplificada de visualizar um arquivo JS no Node pode ser:

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

// your code

return module.exports;

Se você definir uma propriedade exportscomo exports.a = 9;, que será configurada module.exports.atambém porque os objetos são passados ​​como referências em JavaScript, o que significa que, se você definir várias variáveis ​​para o mesmo objeto, elas serão todas o mesmo objeto; então, exportse module.exportssão o mesmo objeto.
Mas se você definir exportsalgo novo, ele não será mais definido como module.exports, portanto, exportse module.exportsnão será mais o mesmo objeto.

ir-ponto-de-ônibus
fonte
11
Certo, são apenas conceitos básicos de tipos de referência.
Vitalii Korsakov 26/09
18
Por quê!? Por que alguém pode ler isso apenas aqui. Esse deve ser o slogan para todo javaScript modular. Obrigado
lima_fil 23/01/15
8
Lindamente explicado!
Aakash Verma
3
impressionante, melhor resposta !!
John
5
Ótima explicação. A documentação para module.exportsdescreve-lo também: nodejs.org/api/modules.html#modules_module_exports
Brian Morearty
52

A resposta de Renee está bem explicada. Além da resposta com um exemplo:

O Nó faz muitas coisas no seu arquivo e um dos importantes é ENVIAR o arquivo. O código-fonte interno nodejs "module.exports" é retornado. Vamos dar um passo atrás e entender o invólucro. Suponha que você tenha

greet.js

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

module.exports = greet;

o código acima é agrupado como IIFE (expressão de função imediatamente chamada) no código-fonte nodejs da seguinte maneira:

(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

e a função acima é invocada (.apply ()) e retornada module.exports. No momento, module.exports e exporta apontando para a mesma referência.

Agora, imagine que você reescreve greet.js como

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

a saída será

[Function]
{}

o motivo é: module.exports é um objeto vazio. Nós não configuramos nada para module.exports, em vez disso, definimos exportações = function () ..... no novo greet.js. Portanto, module.exports está vazio.

Tecnicamente as exportações e module.exports devem apontar para a mesma referência (isso está correto !!). Mas usamos "=" ao atribuir function () .... às exportações, o que cria outro objeto na memória. Portanto, module.exports e export produzem resultados diferentes. Quando se trata de exportações, não podemos substituí-lo.

Agora, imagine que você reescreve (isso é chamado de Mutação) greet.js (consultando a resposta de Renee) como

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

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

a saída será

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

Como você pode ver module.exports e exportações estão apontando para a mesma referência, que é uma função. Se você configurar uma propriedade em exportações, ela será configurada em module.exports porque, em JS, os objetos são passados ​​por referência.

A conclusão é sempre usar module.exports para evitar confusão. Espero que isto ajude. Feliz codificação :)

Sdembla
fonte
Essa também é uma bela resposta perspicaz e complementa a resposta da @ goto-bus-stop. :)
varun
23

Além disso, algumas coisas que podem ajudar 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;

Ótimo, neste caso:

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

Portanto, por padrão, "this" é realmente igual a module.exports.

No entanto, se você alterar sua implementação para:

math.js

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

module.exports = {
    add: add
};

Nesse caso, funcionará bem, no entanto, "this" não é mais igual a module.exports, porque um novo objeto foi criado.

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

E agora, o que será retornado pelo requisito é o que foi definido dentro do módulo. Exportações, não isso ou exportações, mais.

Outra maneira de fazer isso seria:

math.js

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

Ou:

math.js

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

A resposta de René sobre a relação entre exportsemodule.exports é bastante clara: trata-se de referências a javascript. Só quero adicionar que:

Vemos isso em muitos módulos de nós:

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

Isso garantirá que, mesmo se alteramos o module.exports, ainda podemos usar as exportações fazendo essas duas variáveis ​​apontarem para o mesmo objeto.

fengshuo
fonte
Fiquei confuso com essa explicação, tipo elaborar?
27617 GuyFreakz
6
@GuyFreakz eu não tenho certeza se isso fala com sua confusão, mas module.exportse exportssão variáveis apenas separadas, inicializados para referenciar o mesmo objeto. Se você alterar o que uma variável faz referência, as duas variáveis ​​não fazem mais referência à mesma coisa. A linha de código acima garante que ambas as variáveis ​​sejam inicializadas no mesmo novo objeto.
Andrew Palmer
Um caso de uso real que todo mundo perdeu no @fengshuo. Obrigado!
Aakash Verma
0

myTest.js

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

exports.put = function () {};

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

exportse module.exportssão os mesmos e uma referência ao mesmo objeto. Você pode adicionar propriedades de duas maneiras, conforme sua conveniência.

Shashwat Gupta
fonte