Node.js

Node.js モジュール分割についてのメモ

node.jsのモジュールについて、動作を確認してみました。 こんな処にも、ノンブロッキングI/Oの実行モデルの影響があるんですね。 しっかり頭を切り替えて考えないと、バグを埋め込んでしまいます。

モジュールの関数をアクセスする

test1a.js
#!/usr/bin/env node

var app = require('./mod1a');

console.log("app.abc = ", app.abc(1, 2));
console.log("app.xyz = ", app.xyz(2, 3));

関数名をexportsするには、次の書き方になります。

mod1a.js
exports.abc = function(a, b) {
    return a + b;
};

exports.xyz = function(a, b) {
    return a * b;
};

実行結果

$ ./test1a.js 
app.abc =  3
app.xyz =  6

もう一つの書き方

読み難くなりますが、functionを省略して、スッキリ書くこともできます。

mod1b.js
exports.abc = (a, b) => a + b;
exports.xyz = (a, b) => a * b;

コールの方法は同じです。

test1b.js
#!/usr/bin/env node

var app = require('./mod1b');

console.log("app = ", app.abc(1, 2));
console.log("app = ", app.xyz(2, 3));

もう一つの書き方の実行結果

$ ./test1b.js
app =  3
app =  6

さらにカッコを使った書き方

複数行の処理を書きたい場合は、{}カッコで括ることができます。

mod1c.js
exports.abc = (a, b) => {
    c = a + b;
    return c;
}
exports.xyz = (a, b) => {
    c = a * b;
    return c;
}

モジュールのオブジェクトにアクセスする

変数を次の書き方で、エクスポートできます。

mod2.js
var x = 100;
module.exports = x;
test2.js
#!/usr/bin/env node

var app = require('./mod2');
console.log("app = ", app);

実行結果

$ ./test2.js
app =  100

モジュールにモジュールを与えて実行する

前述のmod1をmod3に与えて実行するものです。

test3.js
#!/usr/bin/env node

var app1 = require('./mod1');
var app3 = require('./mod3')(app1);

console.log("app = ", app3);
mod3.js
module.exports = function(app) {
    return app.abc(3, 4);
};

実行結果

$ ./test3.js 
app =  7

モジュールでメインを判別する

require.main === module が false であれば、モジュールとして呼び出されている事を表し、true であればモジュール自身が、ダイレクトに実行されている。

mod4.js
exports.abc = function(a, b) {
    return a + b;
};


if (require.main === module) {
    console.log("result = ", exports.abc(1, 2));
}
test4.js
#!/usr/bin/env node

var app = require('./mod4');

console.log("app = ", app.abc(4, 5));

実行結果

次の前半が、ダイレクトに実行、後半がメインモジュールから実行されるケース

$ node mod4.js 
main =  true
result =  29

$ ./test4.js 
main =  false
app =  9

モジュールの多重取り込みのケース

mod7a.js のモジュールから、mod7b.js モジュールを読み込みます。

mod7a.js
console.log('a starting');
exports.done = false;
const b = require('./mod7b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');

一方、mod7b.js モジュールは、mod7a.jsモジュールを読み込みます。

mod7b.js
console.log('b starting');
exports.done = false;
const a = require('./mod7a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');

メインのモジュールからは、mod7a,mod7b を順番に取り込みます。

test7.js
#!/usr/bin/env node

console.log('main starting');
const a = require('./mod7a.js');
const b = require('./mod7b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);

実行結果

メッセージのmod7a の読み込みと実行を待たずに、mod7b が読み込まれ実行を開始して、mod7bの終了後に、mod7a が実行されています。おそらく、平行で動作していると考えても良いと思います。 モジュールを取り込む場合、逐次実行ではなく、平行実行である事に注意する必要がありますね。

$ ./test7.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true

参考資料

(1) Node.js v6.11.1 Documentation Modules https://nodejs.org/dist/latest-v6.x/docs/api/modules.html#modules_accessing_the_main_module