このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

node.js: モジュールの使い方

LinuxとWindowsにnode.jsをインストールしてみたので、少し使ってみました。

JavaScriptにおいて、機能が欠けていて困る事って何でしょう。クラスがないとかはさしたる問題じゃないと思うのですが、モジュール機構がないのはホントに辛いです。node.jsには、待望のモジュール機構が導入されています。どうやら、CommonJS(http://www.commonjs.org/)という仕様に従っているようですが、僕は仕様を読んだわけではなくて、実際のnode.jsを触って見当を付けただけです。それを以下に書きます。

環境は、WindowsMinGW/MSYS です。


$ echo $HOME
/c/Users/hiyama/Work

$ which node
/c/Users/hiyama/Work/bin/node.exe

$ node --version
v0.5.0-pre

$

今のところ、Webでドウコウという気はなくて、V8のインタプリタとしてだけnode.jsを使っています。

内容:

  1. モジュールとrequire関数
  2. モジュールのサーチパス
  3. 環境変数NODE_PATH(思うようにいかない)
  4. モジュールの作り方
  5. モジュールの階層化
  6. 感想

モジュールとrequire関数

モジュールとは、単にJavaScriptファイルのことです。モジュールを読み込むにはrequire関数を使います。以下で、「>」はnode.jsのプロンプトです。


> require('util')
{ print: [Function],
puts: [Function],
debug: [Function],
error: [Function],
inspect: [Function],
p: [Function],
log: [Function],
exec: [Function],
pump: [Function],
inherits: [Function] }
>

require関数の引数にはモジュール名(の文字列)を渡します。モジュール名は、JavaScriptファイル名から拡張子".js"を除いた名前です。require関数は、変数や関数をプロパティとして持つオブジェクトを返しますが、その値を取っておかないと後で使えません(その場で使うことはできますが; require('util').puts('hello') とか)。


> var util = require('util')
>

require関数の返した値を仮にモジュールオブジェクトと呼ぶことにします。モジュールオブジェクトを保存する変数は何でもかまいませんが、モジュール名と同じ名前の変数に代入すると分かりやすいでしょう。変数に保存したモジュールオブジェクトの変数や関数は、util.puts("hello") のようにして使えます。

モジュールのサーチパス

モジュールはJavaScriptファイルなのですが、そのファイルはどこにあるのでしょう? あるいは、node.jsはどこでファイルを探すのでしょう? その答えは、require関数のpathsプロパティに入っています。


> require.paths
[ 'c:\\Users\\hiyama\\Work\\.node_modules',
'c:\\Users\\hiyama\\Work\\.node_libraries',
'c:\\Users\\hiyama\\Work\\lib\\node' ]
>

JavaScriptの関数はオブジェクトなので、プロパティを持てるのです。require.pathsの値は環境により変わるでしょうが、上の例は現在の僕の環境です。この配列に並んでいるディレクトリがモジュールをサーチする場所です。

モジュール・サーチパスにディレクトリを加えるには、require.pathsを直接操作する方法もありますが、環境変数NODE_PATHを使うほうが便利でしょう。NODE_PATHの話は次節として、直接加えるなら:


> require.paths.push('c:\\Users\\hiyama\\Work\\js')
4
> require.paths
[ 'c:\\Users\\hiyama\\Work\\.node_modules',
'c:\\Users\\hiyama\\Work\\.node_libraries',
'c:\\Users\\hiyama\\Work\\lib\\node',
'c:\\Users\\hiyama\\Work\\js' ]
>

環境変数NODE_PATH(思うようにいかない)

さて、環境変数NODE_PATHの話です。以下、MSYSのbashを使ってますがWindowsでのこと。まずは、環境変数NODE_PATHを設定。


$ NODE_PATH=~/js; export NODE_PATH

$ env | grep NODE
NODE_PATH=/c/Users/hiyama/Work/js

$

node.jsを起動して、確認してみます。


$ node
> require.paths
[ 'c',
'/Users/hiyama/Work/js',
'c:\\Users\\hiyama\\Work\\.node_modules',
'c:\\Users\\hiyama\\Work\\.node_libraries',
'c:\\Users\\hiyama\\Work\\lib\\node' ]
>

あんれ? /c/Users/hiyama/Work/js が、ドライブ名'c'と残りに分割されてしまいました。Windowsの本来のパス表記を使ってみましょう。


$ NODE_PATH='c:\Users\hiyama\Work\js'

$ echo $NODE_PATH
c:\Users\hiyama\Work\js

$ export NODE_PATH

$ node
> require.paths
[ 'c',
'\\Users\\hiyama\\Work\\js',
'c:\\Users\\hiyama\\Work\\.node_modules',
'c:\\Users\\hiyama\\Work\\.node_libraries',
'c:\\Users\\hiyama\\Work\\lib\\node' ]
>

ウーム、ダメだ。bashじゃなくてCMD.EXEにしてみます。


C:\Users\hiyama\Work>set NODE_PATH=c:\Users\hiyama\Work\js

C:\Users\hiyama\Work>echo %NODE_PATH%
c:\Users\hiyama\Work\js

C:\Users\hiyama\Work>node
> require.paths
[ 'c',
'\\Users\\hiyama\\Work\\js',
'C:\\Users\\hiyama\\Work\\.node_modules',
'C:\\Users\\hiyama\\Work\\.node_libraries',
'C:\\Users\\hiyama\\lib\\node' ]
>

これでもダメ。

複数のディレクトリをコロン「:」で区切って指定したらどうなるでしょう。


$ NODE_PATH=/c/js:/c/Users/hiyama/Work/js;export NODE_PATH

$ node
> require.paths
[ 'c',
'\\js;c',
'\\Users\\hiyama\\Work\\js',
'c:\\Users\\hiyama\\Work\\.node_modules',
'c:\\Users\\hiyama\\Work\\.node_libraries',
'c:\\Users\\hiyama\\Work\\lib\\node' ]
>

なんだか奇妙なことになってます。セミコロンならどうかな?


$ NODE_PATH='/js;/Users/hiyama/Work/js'; export NODE_PATH

$ node
> require.paths
[ '/js;/Users/hiyama/Work/js',
'c:\\Users\\hiyama\\Work\\.node_modules',
'c:\\Users\\hiyama\\Work\\.node_libraries',
'c:\\Users\\hiyama\\Work\\lib\\node' ]
>

あれれれれれ、セミコロンだと切れないや。Windowsではセミコロンがパス区切りなんだけどな。

とりあえず、次のようにしておきますわ。


$ NODE_PATH='\Users\hiyama\Work\js'; export NODE_PATH

$ node
> require.paths
[ '\\Users\\hiyama\\Work\\js',
'c:\\Users\\hiyama\\Work\\.node_modules',
'c:\\Users\\hiyama\\Work\\.node_libraries',
'c:\\Users\\hiyama\\Work\\lib\\node' ]
>

モジュールの作り方

ディレクトリ~/js/(Windows本来のパスでは c:\Users\hiyama\Work\js\)がサーチパスに入ったので、~/js/hello.js というファイルを作りました。

// hello.js
var util = require('util');
exports.greeting = "hello";
exports.say = function() {
    util.puts(exports.greeting);
};

これが、helloモジュールとなります。とりあえず使ってみます。


> var hello = require('hello')
> hello.say()
hello
>

うまくいきました。helloはどんなモジュールオブジェクトになっているでしょう?


> hello
{ greeting: 'hello', say: [Function] }
>

greetingとsayというプロパティを持つオブジェクトです。

もう分かりますよね; モジュールファイル内でexportsという名のオブジェクトがモジュールオブジェクトとなり、requireにより返されます。モジュール定義時には見えている変数・関数でも、requireした後は見えなくなるので情報隠蔽ができます。例えば、hello.js内のトップレベル変数utilは見えなくなります。


> typeof require('util')
'object'
> typeof hello.util
'undefined'
>

モジュールの階層化

モジュールの集まりであるパッケージを表現する特別な方法はありませんが、ファイルシステムディレクトリ階層をそのままモジュール階層として使えます。ディレクトリ ~/js/ の下にサブディレクトリ ~/js/my/ を作って、そこに hello.js を入れてみます。hello.jsのgreetingを"Hi!"に変えました。

// my/hello.js
var util = require('util');
exports.greeting = "Hi!";
exports.say = function() {
    util.puts(exports.greeting);
};


> var myhello = require('my/hello')
> myhello.say()
Hi!
>

感想

node.jsの(おそらくはCommonJSの)モジュールの扱いはとても簡単ですね。これだけのメカニズムでも、今までJavaScriptプログラムの構造化に難儀していた人には嬉しい贈り物です。モジュール機構があれば、大きな規模のJavaScriptプログラムも、扱いやすくきれいな構造に編成できるでしょう。