web-dev-qa-db-ja.com

node.jsでのmodule.exportsとexportsの関係

Node.jsモジュールに次の規約が見つかりました。

module.exports = exports = nano = function database_module(cfg) {...}

module.exportsexportsの違い、そしてなぜここで両方が使われているのでしょうか。

658

module.exportsを設定すると、requiredのときにdatabase_module関数を関数のように呼び出すことができます。 nodeがオブジェクトのmodule.exports参照をエクスポートするので、単にexportsを設定しても関数をエクスポートすることはできません。次のコードでは、ユーザーは関数を呼び出すことができません。

module.js

以下は動作しません。

exports = nano = function database_module(cfg) {return;}

module.exportsが設定されている場合、以下は動作します。

module.exports = exports = nano = function database_module(cfg) {return;}

コンソール

var func = require('./module.js');
// the following line will **work** with module.exports
func();

基本的に node.js は、exportsが現在参照しているオブジェクトをエクスポートしませんが、exportsが最初に参照しているオブジェクトのプロパティをエクスポートします。 Node.js はオブジェクトのmodule.exports参照をエクスポートしますが、関数のように呼び出すことができます。


2番目に重要性が低い理由

exportsが以前にエクスポートされたオブジェクトを参照しないようにするために、module.exportsexportsの両方を設定します。両方を設定することで、あなたはexportsを速記として使用し、将来の潜在的なバグを避けることができます。

exports.prop = trueの代わりにmodule.exports.prop = trueを使用すると文字が節約され混乱を避けることができます。

393
William A

質問がずっと前に回答され、受け入れられたにもかかわらず、私はちょうど私の2セントを共有したいです。

あなたはあなたのファイルの最初に(説明のためだけに)次のようなものがあると想像することができます。

var module = new Module(...);
var exports = module.exports;

enter image description here

ですから、module.exportsとNOT exportsは、他の場所からそのモジュールを要求しているときに返されることに注意してください。

だからあなたが何かをするとき:

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

Module.exportsが指すオブジェクトにも2つの関数 'a'と 'b'を追加しているので、返される結果のtypeofobjectになります。{ a: [Function], b: [Function] }

もちろん、この例でexportsの代わりにmodule.exportsを使用している場合と同じ結果になります。

これは、module.exportsをエクスポートされた値のコンテナのように動作させたい場合です。一方、コンストラクタ関数だけをエクスポートしたい場合は、module.exportsまたはexportsを使用することについて知っておくべきことがあります(必要であればmodule.exportsが返され、エクスポートはされません)。

module.exports = function Something() {
    console.log('bla bla');
}

結果を返すtypeofは'function'であり、それを要求してすぐに次のように呼び出すことができます。
var x = require('./file1.js')();は、返された結果を関数に上書きするためです。

しかし、exportsを使用すると、以下のようなものは使用できません。

exports = function Something() {
    console.log('bla bla');
}
var x = require('./file1.js')(); //Error: require is not a function

exportsでは、参照はmodule.exportsが指すオブジェクトをもう「ポイント」していないので、exportsmodule.exportsの間にはもう関係がありません。この場合、module.exportsはまだ返される空のオブジェクト{}を指しています。

他のトピックからの承認された回答も役に立つでしょう: Javascriptは参考文献として渡しますか?

425
Srle

基本的に答えは、モジュールがrequireステートメントによって要求されたときに実際に起こることにあります。モジュールが必要とされるのはこれが初めてだと仮定します。

例えば:

var x = require('file1.js');

file1.jsの内容

module.exports = '123';

上記のステートメントが実行されると、Moduleオブジェクトが作成されます。そのコンストラクタ関数は次のとおりです。

function Module(id, parent) {
    this.id = id;
    this.exports = {};
    this.parent = parent;
    if (parent && parent.children) {
        parent.children.Push(this);
    }

    this.filename = null;
    this.loaded = false;
    this.children = [];
}

ご覧のとおり、各モジュールオブジェクトはexportsという名前のプロパティを持ちます。これがrequireの一部として最終的に返されるものです。

Requireの次のステップは、file1.jsの内容を以下のような無名関数にラップすることです。

(function (exports, require, module, __filename, __dirname) { 
    //contents from file1.js
    module.exports = '123;
});

そして、この無名関数は次のように呼び出されます。ここでmoduleは、以前に作成されたModuleオブジェクトを指します。

(function (exports, require, module, __filename, __dirname) { 
    //contents from file1.js
    module.exports = '123;
}) (module.exports,require, module, "path_to_file1.js","directory of the file1.js");

関数の内部でわかるように、exports仮引数はmodule.exportsを参照しています。要するにそれはモジュールプログラマに提供される便利さです。

しかしながら、この便利さは慎重に行使される必要があります。いずれにせよ、エクスポートに新しいオブジェクトを割り当てようとするならば、確実にこうします。

exports = module.exports = {};

間違った方法 のようにしても、module.exportsはまだモジュールインスタンスの一部として作成されたオブジェクトを指しています。

exports = {};

結果として、上記のexportsオブジェクトに何かを追加してもmodule.exportsオブジェクトには影響がなく、requireの一部としてエクスポートも返されることもありません。

200
Chandu

最初は、module.exports=exportsrequire関数が、module.exportsが参照するオブジェクトを返します。

オブジェクトにプロパティ を追加する場合、exports.a=1を指定すると、module.exportsとexports それでも は同じオブジェクトを参照します。したがって、requireを呼び出してモジュールを変数に割り当てると、変数はプロパティaを持ち、その値は1になります。

しかし、exports=function(){}のように、 override のいずれかであれば、 different になります。exportsは新しいオブジェクトを、module.exportsは元のオブジェクトを参照します。また、ファイルが必要な場合は、module.exportsが新しいオブジェクトを参照しないため、新しいオブジェクトは返されません。

私にとっては、私は新しいプロパティを追加し続けるか、またはそれらの両方を新しいオブジェクトにオーバーライドします。上書きするだけでは正しくありません。そしてmodule.exportsが本当のボスだということを覚えておいてください。

74
cameron

exportsmodule.exportsは、モジュール内でexportsを再割り当てしない限り同じです。

これについて考える最も簡単な方法は、この行がすべてのモジュールの先頭にあることを暗黙的に考えることです。

var exports = module.exports = {};

モジュール内でexportsを再割り当てすると、モジュール内でそれが再割り当てされ、もはやmodule.exportsと等しくなりません。これが、あなたが関数をエクスポートしたいのなら、あなたがしなければならない理由です:

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

単にあなたのfunction() { ... }exportsに代入したならば、あなたはもはやmodule.exportsを指さないようにexportsを再割り当てするでしょう。

毎回module.exportsで関数を参照したくない場合は、次のようにします。

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

module.exportsが一番左の引数です。

exportsにプロパティを付けることは、あなたがそれを再割り当てしていないので同じではありません。これがこれが機能する理由です

exports.foo = function() { ... }
44
dustin.schultz

JavaScriptは参照のコピーによってオブジェクトを渡します

JavaScriptでオブジェクトが参照渡しされる方法とは微妙な違いがあります。

exportsmodule.exportsはどちらも同じオブジェクトを指しています。 exportsは変数、module.exportsはモジュールオブジェクトの属性です。

私はこのようなものを書くとしましょう:

exports = {a:1};
module.exports = {b:12};

exportsmodule.exportsは異なるオブジェクトを指すようになりました。エクスポートを変更してもmodule.exportsは変更されなくなりました。

インポート関数がmodule.exportsを調べるとき、それは{b:12}を得ます

23
superluminary

ちょっとテストしてみると、nodejsのモジュールコードの中では、次のようになるはずです。

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

そう:

1:

exports = function(){}; // this will not work! as it make the exports to some other pointer
module.exports = function(){}; // it works! cause finally nodejs make the module.exports to export.

2:

exports.abc = function(){}; // works!
exports.efg = function(){}; // works!

3:しかし、この場合は

module.exports = function(){}; // from now on we have to using module.exports to attach more stuff to exports.
module.exports.a = 'value a'; // works
exports.b = 'value b'; // the b will nerver be seen cause of the first line of code we have do it before (or later)
11
Lyman Lai

これは node.jsの中のノードモジュールについて書かれた良い説明です book from Manning publication。
アプリケーションで最終的にエクスポートされるのは、 module.exports。
輸出
は、単にグローバルな参照として設定されています。 module.exports これは、最初はプロパティを追加できる空のオブジェクトとして定義されています。そう exports.myFunc ただの略記です module.exports.myFunc

結果として、 輸出 他に設定されている場合、 参照 の間に module.exports そして 輸出 。なぜなら module.exports 本当にエクスポートされるのは 輸出 期待どおりに機能しなくなります。参照できません。 モジュール.exports もう。そのリンクを維持したい場合は、次のようにします。 module.exports 参照 輸出 また次のように:

module.exports = exports = db;
9
Salar

私はいくつかのテストを経験しました、そしてこれは主題にいくらかの光を当てるかもしれないと思います...

app.js

var ...
  , routes = require('./routes')
  ...;
...
console.log('@routes', routes);
...

/routes/index.jsのバージョン:

exports = function fn(){}; // outputs "@routes {}"

exports.fn = function fn(){};  // outputs "@routes { fn: [Function: fn] }"

module.exports = function fn(){};  // outputs "@routes function fn(){}"

module.exports.fn = function fn(){};  // outputs "@routes { fn: [Function: fn] }"

私も新しいファイルを追加しました:

./routes/index.js

module.exports = require('./not-index.js');
module.exports = require('./user.js');

./routes/not-index.js

exports = function fn(){};

./routes/user.js

exports = function user(){};

出力は "@routes {}"です。


./routes/index.js

module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');

./routes/not-index.js

exports = function fn(){};

./routes/user.js

exports = function user(){};

"@routes {fn:{}、user:{}}"という出力が得られます。


./routes/index.js

module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');

./routes/not-index.js

exports.fn = function fn(){};

./routes/user.js

exports.user = function user(){};

user.js{ ThisLoadedLast: [Function: ThisLoadedLast] }に変更すると、出力「@routes {ThisLoadedLast:[Function:ThisLoadedLast]}」が得られます。


しかし、./routes/index.js...を変更した場合.

./routes/index.js

module.exports.fn = require('./not-index.js');
module.exports.ThisLoadedLast = require('./user.js');

./routes/not-index.js

exports.fn = function fn(){};

./routes/user.js

exports.ThisLoadedLast = function ThisLoadedLast(){};

... "@routes {fn:{fn:[関数:fn]}、ThisLoadedLast:{ThisLoadedLast:[関数:ThisLoadedLast]}}"となります。

だから私はあなたのモジュール定義で常にmodule.exportsを使うことを勧めます。

Nodeの内部で何が起こっているのか完全には理解できていませんが、役に立つと確信しているのでもっと理解できる場合はコメントしてください。

- ハッピーコーディング

6
Cody

これは次の結果です。

console.log("module:");
console.log(module);

console.log("exports:");
console.log(exports);

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

enter image description here

また:

if(module.exports === exports){
    console.log("YES");
}else{
    console.log("NO");
}

//YES

注:CommonJS仕様では、publicメンバーを公開するためのexports変数の使用のみが許可されています。したがって、名前付きエクスポートパターンは、CommonJS仕様と本当に互換性のある唯一のパターンです。 module.exportsの使用は、より広範囲のモジュール定義パターンをサポートするためにNode.jsによって提供される拡張機能です。

3
serkan
var a = {},md={};

//まず、exportsとmodule.exportsが同じ空のObjectを指しています

exp = a;//exports =a;
md.exp = a;//module.exports = a;

exp.attr = "change";

console.log(md.exp);//{attr:"change"}

//もしあなたがexpを他のオブジェクトにポイントするのではなく他のオブジェクトにポイントするならば、それは他のオブジェクトに対するプロパティです。 md.expは空になりますObject {}

var a ={},md={};
exp =a;
md.exp =a;

exp = function(){ console.log('Do nothing...'); };

console.log(md.exp); //{}
3
Anson Hwang

このリンクは上記の質問に回答するのに役立ちます。

http://timnew.me/blog/2012/04/20/exports-vs-module-exports-in-node-js/

他の投稿に追加するにはノード内のモジュールシステムは

var exports = module.exports 

コードを実行する前にですから、exports = fooを使いたいときは、おそらくmodule.exports = exports = fooを使いたいでしょうが、exports.foo = fooを使っても大丈夫です。

3

これは Eloquent JavaScript からの抜粋で、どのようにrequire()がその最も単純な形で機能するかを示しています

問題 モジュールが関数などのエクスポートオブジェクト以外の値を直接エクスポートすることはできません。たとえば、モジュールが定義したオブジェクト型のコンストラクタだけをエクスポートしたい場合があります。現在、それはできません。requireは常にエクスポートされた値として作成するexportsオブジェクトを使用するためです。

解決策 別の変数moduleをモジュールに提供します。これは、プロパティexportsを持つオブジェクトです。このプロパティは最初はrequireによって作成された空のオブジェクトを指していますが、他の値をエクスポートするために別の値で上書きすることができます。

function require(name) {
  if (name in require.cache)
    return require.cache[name];
  var code = new Function("exports, module", readFile(name));
  var exports = {}, module = {exports: exports};
  code(exports, module);
  require.cache[name] = module.exports;
  return module.exports;
}
require.cache = Object.create(null);
3
onmyway133

docs から

Exports変数はモジュールのファイルレベルスコープ内で利用可能で、モジュールが評価される前にmodule.exportsの値が割り当てられます。

ショートカットを許可するので、module.exports.f = ...はexports.f = .... のようにもっと簡潔に書くことができます。ただし、新しい値がエクスポートに割り当てられ、module.exportsにバインドされなくなりました。

これは単にmodule.exportsを指す変数です

3
ANewGuyInTown

「モジュールのエクスポートのルートを関数(コンストラクタなど)にする場合や、一度に1つのプロパティを構築するのではなく、1つの割り当てで完全なオブジェクトをエクスポートする場合は、代わりにmodule.exportsに割り当てます。輸出」 - http://nodejs.org/api/modules.html

2
Bob KaKoO

enter image description here

作成する各ファイルはモジュールです。モジュールはオブジェクトです。 exports : {}というプロパティがあり、デフォルトでは空のオブジェクトです。

関数/ミドルウェアを作成し、exports.findById() => { ... }その後requireなどのこの空のエクスポートオブジェクトにアプリのどこにでも追加して使用できます...

controllers/user.js

exports.findById = () => {
    //  do something
}

使用するには、routes.jsで必要です:

const {findyId} = './controllers/user'
1
Ryan Dhungel

2つの方法で1つのモジュールを作りましょう。

一方通行

var aa = {
    a: () => {return 'a'},
    b: () => {return 'b'}
}

module.exports = aa;

第二の方法

exports.a = () => {return 'a';}
exports.b = () => {return 'b';}

そしてこれが require() がモジュールを統合する方法です。

最初の方法:

function require(){
    module.exports = {};
    var exports = module.exports;

    var aa = {
        a: () => {return 'a'},
        b: () => {return 'b'}
    }
    module.exports = aa;

    return module.exports;
}

第二の方法

function require(){
    module.exports = {};
    var exports = module.exports;

    exports.a = () => {return 'a';}
    exports.b = () => {return 'b';}

    return module.exports;
}
1
Dmitry Sergeev

1.exports - >シングルトンユーティリティとして使用
2。モジュールエクスポート - >サービス、モデルなどの論理オブジェクトとして使用

1
riv
  1. module.exportsexportsは同じfunction database_module(cfg) {...}を指しています。

    1| var a, b;
    2| a = b = function() { console.log("Old"); };
    3|     b = function() { console.log("New"); };
    4|
    5| a(); // "Old"
    6| b(); // "New"
    

    3行目のbaに変更できます。出力は逆になります。結論は次のとおりです。

    abは独立しています。

  2. それでmodule.exports = exports = nano = function database_module(cfg) {...}は以下と同等です。

    var f = function database_module(cfg) {...};
    module.exports = f;
    exports = f;
    

    上記はmodule.jsであると仮定します。これはfoo.jsに必要です。 module.exports = exports = nano = function database_module(cfg) {...}の恩恵は今明らかです。

    • foo.jsでは、module.exportsrequire('./module.js')なので、

      var output = require('./modules.js')();
      
    • moduls.jsmodule.exportsの代わりにexportsを使うことができます。

exportsmodule.exportsの両方が同じものを指しているのであれば、嬉しいでしょう。

module.exportsexportsは両方とも、モジュールが評価される前に同じオブジェクトを指します。

モジュールがrequireステートメントを使用して別のモジュールで使用されている場合、module.exportsオブジェクトに追加するプロパティはすべて使用可能になります。 exportsは、同じものに使用できるショートカットです。例えば:

module.exports.add = (a, b) => a+b

書くことと同等です:

exports.add = (a, b) => a+b

したがって、exports変数に新しい値を割り当てない限り問題ありません。このようなことをするとき:

exports = (a, b) => a+b 

新しい値をexportsに割り当てると、エクスポートされたオブジェクトへの参照がなくなり、モジュールに対してローカルのままになります。

使用可能になった初期オブジェクトに新しいプロパティを追加するのではなく、module.exportsに新しい値を割り当てることを計画している場合は、おそらく次のようにすることを検討する必要があります。

module.exports = exports = (a, b) => a+b

Node.jsのWebサイトには、これに関する非常に良い説明があります。

node jsのmodule.jsファイルは、nodeがファイルを実行するたびにmodule.load system.eを実行するために使用され、次のようにjsファイルの内容をラップします。

'(function (exports, require, module, __filename, __dirname) {',+
     //your js file content
 '\n});'

これはur jsのソースコードの中に入っているので、export、require、moduleなどにアクセスできます。jsファイルに書いた機能を別の方法で書く方法は他にはないので、このアプローチが使われます。

その後、nodeはc ++を使用してこのラップされた関数を実行します。その時点で、この関数に渡されたオブジェクトはエクスポートされます。

あなたはこの関数のパラメータのエクスポートとモジュールの内側を見ることができます。実際にエクスポートはモジュールコンストラクター関数のパブリックメンバーです。

次のコードを見てください

このコードをb.jsにコピーしてください。

console.log("module is "+Object.prototype.toString.call(module));
console.log("object.keys "+Object.keys(module));
console.log(module.exports);
console.log(exports === module.exports);
console.log("exports is "+Object.prototype.toString.call(exports));
console.log('----------------------------------------------');
var foo = require('a.js');
console.log("object.keys of foo: "+Object.keys(foo));
console.log('name is '+ foo);
foo();

このコードをa.jsにコピーしてください。

exports.name = 'hello';
module.exports.name = 'hi';
module.exports.age = 23;
module.exports = function(){console.log('function to module exports')};
//exports = function(){console.log('function to export');}

今ノードを使用して実行します

これが出力です

module is [object Object]
object.keys id,exports,parent,filename,loaded,children,paths
{}
true

exportsは[オブジェクトObject]です。

foo:nameのobject.keysはfunction(){console.log( '関数からモジュールへのエクスポート')}関数からモジュールへのエクスポート

a.jsのコメント行を削除し、その行の上の行をコメントアウトしてb.jsの最後の行を削除して実行します。

javaScriptの世界では、パラメータとして渡されたオブジェクトを再割り当てすることはできませんが、その関数のオブジェクトが別の関数のパラメータとして設定されている場合は、その関数のパブリックメンバーを変更できます。

覚えていますか

module.exportsを使用するのは、requireキーワードを使用するときに関数を取得したい場合だけにしてください。上記の例では、foo = require(a.js)とします。関数としてfooを呼び出せることがわかります。

これはノードのドキュメンテーションが「exportsオブジェクトはModuleシステムによって作成されます。時々これは受け入れられない、彼らのモジュールがあるクラスのインスタンスになることを望んでいます。これを行うには望ましいモジュールをmodule.exportsに割り当てます」

0
sidias