Différence entre «module.exports» et «exports» dans le système de modules CommonJs

277

Sur cette page ( http://docs.nodejitsu.com/articles/getting-started/what-is-require ), il est indiqué que "Si vous souhaitez définir l'objet d'exportation sur une fonction ou un nouvel objet, vous devez utilisez l'objet module.exports. "

Ma question est pourquoi.

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

J'ai console.logged le résultat ( result=require(example.js)) et le premier est [Function]le second {}.

Pourriez-vous s'il vous plaît expliquer la raison derrière cela? J'ai lu le post ici: module.exports vs exportations en Node.js . Il est utile, mais n'explique pas la raison pour laquelle il est conçu de cette manière. Y aura-t-il un problème si la référence des exportations est retournée directement?

Xiao Peng - ZenUML.com
la source
11
Utilisez toujours module.exports.
Gabriel Llamas
1
Je pense que suivre les conseils mentionnés ci-dessus permet d'éviter ce problème.
Vitalii Korsakov du
@GabrielLlamas alors pourquoi de nombreux packages utilisent-ils simplement exports, par exemple github.com/tj/consolidate.js/blob/master/lib/consolidate.js ?
CodyBugstein
3
@Imray Si vous utilisez toujours module.exports, vous ne serez jamais tort, mais vous pouvez utiliser exportssi vous n'êtes pas remplacer la valeur par défaut objet exporté, qui est, si vous attachez simplement des propriétés comme ceci: var foo = require('foo').foo. Cette foopropriété peut être exportée comme ceci: exports.foo = ...et bien sûr aussi avec module.exports. C'est un choix personnel mais j'utilise actuellement module.exportset de exportsmanière appropriée.
Gabriel Llamas
Je préfère exports.myFunc = function () {} donc je n'ai pas à maintenir une liste d'exportations au bas du fichier. Cela se rapproche de la pratique courante d'exportation lorsque vous déclarez dans ES6.
SacWebDeveloper

Réponses:

626

moduleest un objet JavaScript simple avec une exportspropriété. exportsest une simple variable JavaScript qui se trouve être définie sur module.exports. À la fin de votre fichier, node.js «reviendra» essentiellement module.exportsà la requirefonction. Une façon simplifiée d'afficher un fichier JS dans Node pourrait être la suivante:

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

// your code

return module.exports;

Si vous définissez une propriété sur exports, par exemple exports.a = 9;, elle sera également définie module.exports.acar les objets sont transmis en tant que références en JavaScript, ce qui signifie que si vous définissez plusieurs variables sur le même objet, elles sont toutes le même objet; alors exportset module.exportssont le même objet.
Mais si vous définissez exportsquelque chose de nouveau, il ne sera plus mis à module.exports, donc , exportset module.exportsne sont plus le même objet.

goto-bus-stop
la source
11
À droite, ce ne sont que les bases des types de référence.
Vitalii Korsakov
18
Pourquoi!? Pourquoi on ne peut lire ceci qu'ici. Cela devrait être le slogan pour chaque javaScript modulaire. Merci
lima_fil
8
Magnifiquement expliqué!
Aakash Verma
3
génial, meilleure réponse !!
John
5
Grande explication. La documentation de le module.exportsdécrit aussi: nodejs.org/api/modules.html#modules_module_exports
Brian Morearty
52

La réponse de Renée est bien expliquée. Ajout à la réponse avec un exemple:

Node fait beaucoup de choses sur votre fichier et l'un des plus importants est d'ENVELOPPER votre fichier. Le code source de nodejs "module.exports" est retourné. Permet de prendre du recul et de comprendre le wrapper. Supposons que vous ayez

salue.js

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

module.exports = greet;

le code ci-dessus est encapsulé comme IIFE (expression de fonction immédiatement invoquée) dans le code source de nodejs comme suit:

(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

et la fonction ci-dessus est invoquée (.apply ()) et renvoyée module.exports. Pour le moment, module.exports et exports pointent vers la même référence.

Maintenant, imaginez que vous réécrivezreet.js comme

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

la sortie sera

[Function]
{}

la raison en est: module.exports est un objet vide. Nous n'avons pas défini quoi que ce soit sur module.exports, nous avons plutôt défini exports = function () ..... dans le nouveau message d'accueil.js. Ainsi, module.exports est vide.

Techniquement, les exportations et module.exports doivent pointer vers la même référence (c'est correct !!). Mais nous utilisons "=" lors de l'attribution de la fonction () .... aux exportations, ce qui crée un autre objet dans la mémoire. Ainsi, module.exports et exports produisent des résultats différents. En ce qui concerne les exportations, nous ne pouvons pas passer outre.

Maintenant, imaginez que vous réécrivez (cela s'appelle Mutation)

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

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

la sortie sera

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

Comme vous pouvez le voir, module.exports et exports pointent vers la même référence qui est une fonction. Si vous définissez une propriété sur les exportations, elle sera définie sur module.exports car dans JS, les objets sont passés par référence.

La conclusion est toujours d'utiliser module.exports pour éviter toute confusion. J'espère que cela t'aides. Bon codage :)

Sdembla
la source
Cela aussi est une belle réponse perspicace et complète la réponse de @ goto-bus-stop. :)
varun
23

Aussi, une chose qui peut aider à comprendre:

math.js

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

client.js

var math = require('./math');
console.log(math.add(2,2); // 4;

Super, dans ce cas:

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

Ainsi, par défaut, "ceci" est en fait égal à module.exports.

Cependant, si vous changez votre implémentation en:

math.js

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

module.exports = {
    add: add
};

Dans ce cas, cela fonctionnera bien, cependant, "ceci" n'est plus égal à module.exports, car un nouvel objet a été créé.

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

Et maintenant, ce qui sera retourné par l'exigence est ce qui a été défini à l'intérieur du module.exports, et non plus celui-ci ou les exportations.

Une autre façon de procéder serait:

math.js

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

Ou:

math.js

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

La réponse de René à propos de la relation entre exportset module.exportsest assez claire, il s'agit de références javascript. Je veux juste ajouter cela:

Nous voyons cela dans de nombreux modules de nœuds:

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

Cela garantira que même si nous avons modifié module.exports, nous pouvons toujours utiliser les exportations en faisant pointer ces deux variables vers le même objet.

fengshuo
la source
Je suis devenu confus avec cette explication, aimable à élaborer?
GuyFreakz
6
@GuyFreakz Je ne sais pas si cela parle à votre confusion, mais module.exportset ne exportssont que des variables distinctes, initialisés pour référencer le même objet. Si vous modifiez les références d'une variable, les deux variables ne font plus référence à la même chose. La ligne de code ci-dessus garantit que les deux variables sont initialisées sur le même nouvel objet.
Andrew Palmer
Un cas d'utilisation réel que tout le monde a raté sur @fengshuo. Merci!
Aakash Verma
0

myTest.js

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

exports.put = function () {};

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

exportset module.exportssont les mêmes et une référence au même objet. Vous pouvez ajouter des propriétés dans les deux sens selon votre convenance.

Shashwat Gupta
la source