2015/01/07更新

[NodeJS] モジュール定義について学ぶ

このエントリーをはてなブックマークに追加      

明けましておめでとうございます、@yoheiMuneです。今年もどうぞ宜しくお願いします。
本年1つ目の記事は、node.jsにおけるモジュール定義についてブログを書きたいと思います。

画像

Special Thanks to https://flic.kr/p/7sQ4oC




目次




node.jsにおけるモジュール定義

node.jsではcommon.jsのモジュールに準拠したモジュール定義を行うことができ、それを別ファイルからrequireメソッドを使って呼び出すことができます。扱いたいことはたったこれだけなのですが、具体例がないと理解しづらいので、具体例を出しつつnode.jsのモジュール定義を学べたらと思います。


その1:exportsに関数を代入する

モジュールの定義はオブジェクト型でも関数型でも(時には文字列型でも)行う事ができます。関数を外部に公開する場合には、次のように定義して利用します。
// モジュール定義側(sub1.js)
module.exports = function (a, b) {
    return a + b;
};
// モジュールを利用する側(index.js)
var add = require('./sub1.js'); // require('./sub1')でも良い
var result = add(1, 2);
console.log(result); // => 3
シンプルですね。
しかし1モジュール1関数のみを提供することは珍しいかもしれません。多くの場合、次のようなオブジェクト形式を用いて複数の機能を一括で公開する方式が用いられます。



その2:exportsにオブジェクトを代入する

モジュール定義では、オブジェクト形式での機能を公開することができます。例えば以下のように実装します。
// モジュール定義側(sub2.js)
module.exports = {
    add: function (v1, v2) {
        return v1 + v2;
    },
    minus: function (v1, v2) {
        return v1 - v2;
    }
};
// モジュールを利用する側(index.js)
var calc = require('./sub2');

var result1 = calc.add(1, 2);
console.log(result1); // => 3

var result2 = calc.minus(1, 2);
console.log(result2); // => -1
また、module.exportsには以下のように代入することもできます。
module.exports.add = function (v1, v2) {
    return v1 + v2;  
};
module.exports.minus = function (v1, v2) {
    return v1 - v2;  
};
どちらの書き方が良いかは、個人の好みやプロジェクトのコーディングルールに依りますが、このようなオブジェクト形式でモジュール定義を行うと便利です。



ライブラリっぽくモジュールを定義する

require関数の引数にはディレクトリを指定することも可能です。以下のディレクトリ構成の場合に、

ディレクトリ構成

  • app.js
  • module/
    • index.js
    • calc.js
    • print.js

以下のようなrequireを行うと、対象ディレクトリのindex.jsが読み込まれます。
var myModule = require('./module');
// module/index.jsが読み込まれる
そして読み込まれるindex.jsにおいてcalc.jsprint.jsを読み込むことで、requireする側はmodule/を読み込むだけでcalc.jsprint.jsの機能を使えるようになります。
// module/index.js
module.exports = {
    calc: require('./calc'),
    print: require('./print')
};
// 利用側は、./moduleを読み込むだけで、
// module/calcやmodule/printの機能を利用することができて便利です
var myModule = require('./module');

// module/calc.jsの機能を利用する
var result = myModule.calc.add(1, 2);

// module/print.jsの機能を利用する
myModule.print.sayHello();
こんな感じで、requiremodule.exportsを使いこなせるようになると良いですね!



余談1:requireのパスの読み込みを簡略化する方法

node.jsで実装していると、しばしば以下のようなrequireを書く必要がありなかなか大変です。
var foo = require('../../../aa/bb/cc/foo.js');
node.jsを利用する他の開発者も同じように困っているようで、Better local require() paths for Node.js(英語)では以下のような解決策が示されています。

  • node_modules/ディレクトリにシンボリックリンクを貼る
  • global.__baseにアプリケーションルートのパスを設定する
  • rekuireというnpmパッケージを利用する
  • 環境変数のNODE_PATHでルートディレクトリを指定する
  • ほか

個人的にはglobal.__baseがいいかなーと思いますが、どの解決策を選ぶかは時と場合によりそうです。



余談2:module.exportsとexportsの違い

node.js api(日本語)によると同じものと考えて良いようです。exportsmodule.exportsへの参照であり、エイリアスであるとのこと。Node.js上では、以下のような実装で開発者の定義したモジュールが呼び出されていると考えると良いようです。
function require(...) {
  // ...
  function (module, exports) {

    // 自分で定義したモジュールがここで実行される
    // そのため、moduleやexportsが利用できるし、requireも利用できる

  } (module, module.exports);
  return module;
}
細かく見れば使い分けがあるようですが、APIドキュメントは以下のように言及しています。
もしexportsmodule.exportsの間の関係が魔法のように見えるなら、exportsを無視してmodule.exportsだけを使うようにしてください。
なるほどです。



余談3:requireの結果はキャッシュされる

requireで呼び出すモジュールの実行結果はキャッシュされます。例えば以下のようなモジュールがあるとします。
// sub1.js
console.log('sub1 was executed.');
module.exports = function () {
    console.log('Hello from sub1');
};
上記のsub1.jsrequireで以下のように呼び出しても、sub1.jsが実行されるのは一度限りです。
var sub1 = require('./sub1');
var sub1 = require('./sub1'); // 2回目の呼び出し
// 実行結果
sub1 was executed. // 1回しか出力されない
モジュールを定義する場合に、上記のような振る舞いになることを頭に片隅に置いておくと、不明なバグを減らせそうです。



参照リンク

この記事を書くために以下のページを参照しました。貴重な情報をありがとうございます。

- Node.js v0.11.11 マニュアル & ドキュメンテーション

- Better local require() paths for Node.js(英語)



最後に

今回はnodeのモジュール定義についてブログを書きました。理解できるとナンテコトナイですが、理解できるまでがなかなか大変ですよね。少しでも何かの助けになれば幸いです。

本ブログでは、フロントエンドに関する情報を中心に発信していきます。気になった方はぜひ、本ブログのRSSTwitterをフォローして頂けると幸いです ^ ^。

最後までご覧頂きましてありがとうございました!





こんな記事もいかがですか?

RSS画像

もしご興味をお持ち頂けましたら、ぜひRSSへの登録をお願い致します。