Unterschied zwischen "module.exports" und "exports" im CommonJs Module System


276

Auf dieser Seite ( http://docs.nodejitsu.com/articles/getting-started/what-is-require ) heißt es: "Wenn Sie das Exportobjekt auf eine Funktion oder ein neues Objekt setzen möchten, müssen Sie dies tun." Verwenden Sie das Objekt module.exports. "

Meine Frage ist warum.

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

Ich habe das Ergebnis ( result=require(example.js)) konsolidiert und das erste ist [Function]das zweite {}.

Könnten Sie bitte den Grund dafür erklären? Ich lese die Post hier: module.exports vs Exporte in Node.js . Es ist hilfreich, erklärt aber nicht den Grund, warum es so gestaltet ist. Wird es ein Problem geben, wenn die Referenz der Exporte direkt zurückgegeben wird?


11
Immer benutzen module.exports.
Gabriel Llamas

1
Ich denke, das Befolgen der oben genannten Ratschläge ermöglicht es, dieses Problem zu vermeiden.
Vitalii Korsakov

@GabrielLlamas also warum nutzen viele Pakete nur exportszum Beispiel github.com/tj/consolidate.js/blob/master/lib/consolidate.js ?
CodyBugstein

3
@Imray Wenn Sie immer verwenden module.exports, werden Sie sich nie irren, aber Sie können verwenden, exportswenn Sie das exportierte Standardobjekt nicht ersetzen, dh wenn Sie einfach Eigenschaften wie diese anhängen : var foo = require('foo').foo. Diese fooEigenschaft kann folgendermaßen exportiert werden: exports.foo = ...und natürlich auch mit module.exports. Es ist eine persönliche Entscheidung, aber ich benutze module.exportsund exportsangemessen.
Gabriel Llamas

Ich bevorzuge exports.myFunc = function () {}, damit ich keine Liste der Exporte am Ende der Datei führen muss. Es fühlt sich näher an der üblichen Praxis des Exportierens an, wenn Sie in ES6 deklarieren.
SacWebDeveloper

Antworten:


624

moduleist ein einfaches JavaScript-Objekt mit einer exportsEigenschaft. exportsist eine einfache JavaScript-Variable, auf die zufällig gesetzt wird module.exports. Am Ende Ihrer Datei kehrt node.js grundsätzlich module.exportszur requireFunktion zurück. Eine vereinfachte Möglichkeit zum Anzeigen einer JS-Datei in Node könnte sein:

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

// your code

return module.exports;

Wenn Sie eine Eigenschaft exportswie exports.a = 9;diese festlegen, wird diese ebenfalls festgelegt, module.exports.ada Objekte in JavaScript als Referenzen übergeben werden. Wenn Sie also mehrere Variablen auf dasselbe Objekt festlegen, handelt es sich bei allen um dasselbe Objekt. also dann exportsund module.exportssind das gleiche Objekt.
Aber wenn Sie setzen exportsneue zu etwas, es wird nicht mehr eingestellt werden module.exports, so exportsund module.exportsnicht mehr das gleiche Objekt.


11
Richtig, es sind nur Grundlagen von Referenztypen.
Vitalii Korsakov

18
Warum!? Warum kann man das nur hier lesen. Dies sollte ein Slogan für jedes modulare JavaScript sein. Danke
lima_fil

8
Schön erklärt!
Aakash Verma

3
Super, beste Antwort !!
John

5
Tolle Erklärung. Die Dokumentation für module.exportsbeschreibt es auch: nodejs.org/api/modules.html#modules_module_exports
Brian Morearty

52

Renees Antwort ist gut erklärt. Ergänzung zur Antwort mit einem Beispiel:

Der Knoten macht eine Menge Dinge mit Ihrer Datei und einer der wichtigsten ist das WRAPPEN Ihrer Datei. Innerhalb des Knotens wird der Quellcode "module.exports" zurückgegeben. Machen wir einen Schritt zurück und verstehen den Wrapper. Angenommen, Sie haben

greet.js

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

module.exports = greet;

Der obige Code wird wie folgt als IIFE (Sofort aufgerufener Funktionsausdruck) in den Quellcode des Knotens eingeschlossen:

(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

und die obige Funktion wird aufgerufen (.apply ()) und module.exports zurückgegeben. Zu diesem Zeitpunkt exportiert module.exports und exportiert auf dieselbe Referenz.

Stellen Sie sich vor, Sie schreiben greet.js als neu

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

die Ausgabe wird sein

[Function]
{}

Der Grund ist: module.exports ist ein leeres Objekt. Wir haben nichts auf module.exports gesetzt, sondern export = function () ..... in new greet.js. Modul.exports ist also leer.

Technisch gesehen sollten Exporte und module.exports auf dieselbe Referenz verweisen (das ist richtig !!). Wir verwenden jedoch "=", wenn wir Exporten function () .... zuweisen, wodurch ein weiteres Objekt im Speicher erstellt wird. Module.exports und Exporte führen also zu unterschiedlichen Ergebnissen. Wenn es um Exporte geht, können wir es nicht überschreiben.

Stellen Sie sich nun vor, Sie schreiben greet.js (dies bezieht sich auf die Antwort von Renee) als (dies wird als Mutation bezeichnet) neu

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

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

die Ausgabe wird sein

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

Wie Sie sehen können, zeigen module.exports und exports auf dieselbe Referenz, die eine Funktion ist. Wenn Sie für Exporte eine Eigenschaft festlegen, wird diese für module.exports festgelegt, da in JS Objekte als Referenz übergeben werden.

Fazit: Verwenden Sie immer module.exports, um Verwirrung zu vermeiden. Hoffe das hilft. Viel Spaß beim Codieren :)


Auch dies ist eine schöne aufschlussreiche Antwort und ergänzt die Antwort von @ goto-bus-stop. :)
Varun

23

Auch eines, das zum Verständnis beitragen kann:

math.js

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

client.js

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

Großartig, in diesem Fall:

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

Daher entspricht "this" standardmäßig tatsächlich module.exports.

Wenn Sie jedoch Ihre Implementierung ändern in:

math.js

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

module.exports = {
    add: add
};

In diesem Fall funktioniert es einwandfrei, "this" ist jedoch nicht mehr gleich module.exports, da ein neues Objekt erstellt wurde.

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

Und jetzt wird von der Anforderung zurückgegeben, was in module.exports definiert wurde, nicht mehr dies oder exportiert.

Ein anderer Weg wäre:

math.js

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

Oder:

math.js

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

15

Renes Antwort auf die Beziehung zwischen exportsund module.exportsist ziemlich klar, es geht nur um Javascript-Referenzen. Ich möchte nur hinzufügen, dass:

Wir sehen dies in vielen Knotenmodulen:

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

Dadurch wird sichergestellt, dass wir auch dann Exporte verwenden können, wenn wir module.exports geändert haben, indem diese beiden Variablen auf dasselbe Objekt verweisen.


Ich wurde mit dieser Erklärung verwirrt, freundlich zu erarbeiten?
GuyFreakz

6
@GuyFreakz Ich bin mir nicht sicher , ob dies auf Ihre Verwirrung spricht, aber module.exportsund exportssind nur getrennte Variablen initialisiert das gleiche Objekt zu verweisen. Wenn Sie ändern, auf welche eine Variable verweist, verweisen die beiden Variablen nicht mehr auf dasselbe. Die obige Codezeile stellt sicher, dass beide Variablen mit demselben neuen Objekt initialisiert werden.
Andrew Palmer

Ein tatsächlicher Anwendungsfall, den alle anderen bei @fengshuo verpasst haben. Vielen Dank!
Aakash Verma

0

myTest.js

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

exports.put = function () {};

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

exportsund module.exportssind gleich und ein Verweis auf das gleiche Objekt. Sie können Eigenschaften nach Belieben auf beide Arten hinzufügen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.