web-dev-qa-db-ja.com

CommonJsモジュールシステムにおける "module.exports"と "exports"の違い

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

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

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

私はconsole.logに結果(result=require(example.js))を書き、最初のものは[Function]、二番目のものは{}です。

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

232

moduleは、exportsプロパティを持つ単純なJavaScriptオブジェクトです。 exportsは、たまたまmodule.exportsに設定されているプレーンなJavaScript変数です。あなたのファイルの終わりに、node.jsは基本的にrequire関数にmodule.exportsを「返す」でしょう。 NodeでJSファイルを見るための簡単な方法はこれでしょう:

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

// your code

return module.exports;

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

537
goto-bus-stop

レニーの答えはよく説明されています。例を挙げて答えを追加します。

Nodeはあなたのファイルにたくさんのことをします、そして重要なことの一つはあなたのファイルをラッピングすることです。内部nodejsソースコード "module.exports"が返されます。一歩戻り、ラッパーを理解しましょう。あなたが持っているとします

greet.js

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

module.exports = greet;

上記のコードは、次のようにnodejsソースコード内でIIFE(即時起動関数式)としてラップされています。

(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とexportは同じ参照を指しています。

では、greet.jsを次のように書き直すとしましょう。

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

出力は

[Function]
{}

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

技術的にエクスポートとmodule.exportsは同じ参照を指すべきです(それは正しいです!!)。しかし、function()....をエクスポートに代入するときは "="を使用します。これにより、メモリ内に別のオブジェクトが作成されます。そのため、module.exportsとexportの結果は異なります。輸出に関しては、それを上書きすることはできません。

さて、あなたが(これはミューテーションと呼ばれる)greet.js(Renee answerを参照)を次のように書き直すと想像してください。

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を使うことです。お役に立てれば。ハッピーコーディング:)

39
Sdembla

また、理解するのに役立つかもしれない一つのこと:

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;
};
19
Rodrigo Branas

exportsmodule.exportsの関係についてのReneの答えは明らかです。それはすべてJavaScriptリファレンスについてのものです。それを追加したいだけです:

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

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

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

13
fengshuo