CommonJsモジュールシステムの「module.exports」と「exports」の違い


277

このページ(http://docs.nodejitsu.com/articles/getting-started/what-is-require)には、「exportsオブジェクトを関数または新しいオブジェクトに設定する場合は、 module.exportsオブジェクトを使用してください。」

私の質問はその理由です。

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

私はconsole.logに結果(result=require(example.js))を記録し、最初の結果[Function]は2番目の結果です{}

その理由を教えてください。Node.jsでのmodule.exportsとexportsの投稿をここで読みました。それは役に立ちますが、そのように設計されている理由を説明していません。輸出の参照を直接返すと問題はありますか?


11
常に使用してくださいmodule.exports
Gabriel Llamas 2013

1
上記のアドバイスに従うと、この問題を回避できると思います。
Vitalii Korsakov 2013

@GabrielLlamasでは、なぜ多くのパッケージが単にgithub.com/tj/consolidate.js/blob/master/lib/consolidate.jsexportsなどを使用するのですか?
CodyBugstein、2015

3
@Imrayを常に使用する場合module.exports、決して間違いexportsはありませんが、デフォルトのエクスポートされたオブジェクトを置き換えない場合、つまり、次のようなプロパティを単にアタッチする場合に使用できますvar foo = require('foo').foo。このfooプロパティは次のようにエクスポートできます。exports.foo = ...もちろん、でもエクスポートできますmodule.exports。それは個人的な選択ですが、私は現在適切に使用module.exportsしていexportsます。
Gabriel Llamas、2015

私はexports.myFunc = function(){}を好むので、ファイルの下部にエクスポートのリストを保持する必要はありません。ES6で宣言すると、エクスポートの一般的な方法に近い感じがします。
SacWebDeveloper

回答:


625

moduleexportsプロパティを持つプレーンJavaScriptオブジェクトです。exportsはたまたま設定されるプレーンなJavaScript変数ですmodule.exports。ファイルの最後で、node.jsは基本的module.exportsrequire関数に「戻ります」。NodeでJSファイルを表示する簡単な方法は次のとおりです。

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

// your code

return module.exports;

exportsようにexports.a = 9;にプロパティを設定するとmodule.exports.a、JavaScriptでオブジェクトが参照として渡されるため、プロパティも設定されます。つまり、同じオブジェクトに複数の変数を設定すると、それらすべて同じオブジェクトになります。それでexportsそしてmodule.exports同じオブジェクトです。
あなたが設定されている場合でも、exports新しい何かに、それはもはやに設定されませんmodule.exportsので、exportsmodule.exportsもはや同じオブジェクトです。


11
そう、それは参照型の基本にすぎません。
Vitalii Korsakov 2013

18
なぜ!?なぜここでしか読めないのか。これは、すべてのモジュラーjavaScriptのキャッチフレーズになるはずです。ありがとう
lima_fil

8
美しく説明しました!
Aakash Verma 2018

3
素晴らしい、ベストアンサー!!
John

5
素晴らしい説明。のドキュメントでもmodule.exports説明しています:nodejs.org/api/modules.html#modules_module_exports
Brian Morearty

52

レニーの答えはよく説明されています。例を含む回答への追加:

Nodeはファイルに対して多くのことを行い、重要なことの1つはファイルをラップすることです。nodejs内のソースコード「module.exports」が返されます。一歩下がってラッパーを理解しましょう。あなたが持っていると仮定します

greet.js

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

module.exports = greet;

上記のコードは、次のようにnodejsソースコード内でIIFE(Immediately Invoked Function Expression)としてラップされています。

(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

上記の関数が呼び出され(.apply())、module.exportsが返されます。現時点では、同じ参照を指すmodule.exportsおよびexportsです。

さて、greet.jsを次のように書き直してみてください。

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

出力は

[Function]
{}

理由は次のとおりです。module.exportsは空のオブジェクトです。module.exportsには何も設定せず、exports = function().....を新しいgreet.jsに設定しました。したがって、module.exportsは空です。

技術的には、exportsとmodule.exportsは同じ参照を指す必要があります(正解です!!)。しかし、function()....をエクスポートに割り当てるときに「=」を使用すると、メモリ内に別のオブジェクトが作成されます。したがって、module.exportsとexportsは異なる結果を生成します。エクスポートに関しては、オーバーライドすることはできません。

ここで、greet.js(Reneeの回答を参照)を次のように書き直し(これをMutationといいます)するとします

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

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

出力は

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

ご覧のとおり、module.exportsとexportsは関数である同じ参照を指しています。エクスポートでプロパティを設定すると、JSではオブジェクトが参照渡しされるため、module.exportsでプロパティが設定されます。

結論は混乱を避けるために常にmodule.exportsを使用することです。お役に立てれば。ハッピーコーディング:)


これもまた、洞察に満ちた美しい回答であり、@ goto-bus-stopの回答を補完します。:)
varun

23

また、理解に役立つかもしれない1つの事柄:

math.js

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

client.js

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

この場合、すばらしい:

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

したがって、デフォルトでは、「this」は実際にはmodule.exportsと同じです。

ただし、実装を次のように変更した場合:

math.js

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

module.exports = {
    add: add
};

この場合、問題なく機能しますが、新しいオブジェクトが作成されたため、「this」はmodule.exportsと同じではなくなりました。

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

そして、requireによって返されるのは、もはやmodule.exports内で定義されたものであり、これやエクスポートではありません。

それを行う別の方法は次のとおりです。

math.js

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

または:

math.js

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

15

exportsとの関係についてのルネの答えmodule.exportsははっきりしています。それはすべてJavaScriptリファレンスに関するものです。それを追加したいだけです:

これは多くのノードモジュールで見られます。

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

これにより、module.exportsを変更した場合でも、これら2つの変数が同じオブジェクトを指すようにすることで、エクスポートを使用できるようになります。


私はこの説明に混乱しました、詳しく説明してくれませんか?
GuyFreakz 2017

6
これはあなたの混乱に話すが、場合@GuyFreakzは私はわからないmodule.exportsし、exports同じオブジェクトを参照するように初期化だけで別々の変数、です。1つの変数が参照するものを変更すると、2つの変数は同じものを参照しなくなります。上記のコード行により、両方の変数が同じ新しいオブジェクトに初期化されます。
Andrew Palmer

@fengshuoで他のすべての人が見逃した実際の使用例。ありがとう!
Aakash Verma

0

myTest.js

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

exports.put = function () {};

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

exportsおよびmodule.exportsは同じであり、同じオブジェクトへの参照です。必要に応じて、両方の方法でプロパティを追加できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.