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

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

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

参照用 記事

JS:JSANモジュールシステムを使おう

行きがかり上、JSANモジュールシステムの説明をしておきます。

[追記 dateTime="夕方"]: typoと、本文の趣旨とサンプルコードがずれているところがあったので修正しました。修正の内容は、このエントリーの最後にあります。[/追記]

JSAN(JavaScript Archive Network)は、JavaScriptライブラリ群を集積したアーカイブ・サイトです。そして、JSANがベースとしているモジュール(あるいはパッケージ)システムがJSAN.jsというスクリプトで提供されています。

僕は、モジュールシステムの必要性を強く感じていたので、試しに自作してみたのですが、既に使用されているJSAN.jsがあるので、こっちを使うことに決めました。

この記事と一緒に、次のbrazilさんのエントリーを参照することをお勧めします。

内容:

  1. こんな事例を考える
  2. モジュールとモジュールパス
  3. JSANモジュールシステムを導入しよう
  4. 利用するときの注意事項
  5. モジュール側のお約束
  6. まとめ

●こんな事例を考える

JSANモジュールシステムは、Perlのモジュールシステムの影響を強く受けています。ですから、Perl使いにはお馴染みの仕掛けでしょう。ここでは、Perlの知識を仮定せず、用語/概念も単純化(一部は省略)して説明します。

まずは事例から。Webサーバーに次のようなディレクトリ構造があったとします。


[.]
+---mypage.html
+---myscript.js
+---[lib]
+--Util.js
+---[Search]
+--Fast.js

mypage.htmlでmyscript.jsを使います。つまり:



という記述が、mypage.htmlに必要ですね。

さらに、myscript.jsがSearch/Fast.jsを必要として、Search/Fast.jsがlib/Util.jsを必要としているとしましょう。すべてのJavaScriptプログラムをキチンと揃えるために、次のタグを並べる必要があります。





別にどうってことない? いやいや、".js"ファイル数が増えたり、プログラム(スクリプト)の依存関係が複雑になると、「キチンとscriptタグを並べる」管理作業は悪夢になり得ます。

●モジュールとモジュールパス

他のプログラムから使われることを意図したJavaScriptソースファイルをモジュールと呼びます。わかりやすいように、またJSANの習慣に従い、モジュールのファイル名は大文字からはじめることにします(メカニズム上は、命名は自由だが)。

モジュールの、“ある特定のディレクトリ”を基準とした相対パスモジュールパスといいます。例えば、先の図の[.]を基準ディレクトリとするなら、Search/Fast.js はモジュールパスです。

では、lib/Util.js もモジュールパスでしょうか? (JSAN.jsの通常の使用法では)違います。./lib/Util.jsファイルのモジュールパスはUitl.jsです。なぜかというと、基準ディレクトリが前もって複数設定されているからです。デフォルトでは、"." と "lib"が基準ディレクトリです。


// JSANソースコード抜粋
JSAN.includePath = ['.', 'lib']; //必要なら変更する

このため、Util.jsは、./Util.js → ./lib/Uitl.js の順で探すことになるので、Util.jsをモジュールパスとしてもOKなのです。ちなみに、Search/Fast.js は、./Search/Fast.js → ./lib/Search/Fast.js と探すので、libの下に置いてもかまいません。

JSANモジュールシステムを導入しよう

モジュールパスの概念はわかりましたか? モジュールパスから拡張子「.js」を取り除いて、「/」を「.」に置き換えた名前をモジュール名と呼びます。例えば、Search.Fastはモジュール名です。

JSANの習慣では、モジュール名+バージョンがモジュールを特定するグローバル識別子として使われます。配布ファイルの命名は、Search.Fast-0.21.tar.gz またはSearch-Fast-0.21.tar.gz です。

さて、JSANとDebug.Logger(実在のモジュール)を次のように配置したと仮定して話を進めます。


[..]
+-- [jsan]
+---JSAN.js
+---[Debug]
+---Logger.js
+-- [.]
+---mypage.html
+---myscript.js
+---[lib]
+--Util.js
+---[Search]
+--Fast.js

こうすると、mypage.html内の記述は次のようになります。




他のモジュールへの依存関係は、myscript.js内に次のように書きます。必要なモジュールUtilは、Search.Fastからロードされます。


/* myscript.js */
JSAN.use("Search.Fast");

使うモジュールを変更するときも、もはやHTMLファイルを修正する必要はありません。myscript.jsを変更するだけです。例えば:


/* myscript.js */
JSAN.use("Search.Fast");
JSAN.use("Debug.Logger");

アレレッ? Debug.Loggerもちゃんとロードできるの? 大丈夫です。JSAN.jsが存在するディレクトリ(この例では ../jsan/)は自動的にJSAN.includePathに追加されます。つまり、Debug.Loggerのファイルは次の順で検索されます。

  1. ./Debug/Logger.js
  2. ./lib/Debug/Logger.js
  3. ../jsan/Debug/Logger.js

●利用するときの注意事項

ほとんどの言語では、include, import, use, require, consult, loadなどのキーワードにより、他のモジュールの使用/取り込みを指示できます。JSAN.jsは、JavaScriptでもモジュールの取り込み機能を利用できるようにしたものです。

ただし、JSAN.useは宣言ではなくて実行文になります。失敗することもあるので、次のように書くのが無難でしょう。(デバッグ中はthrowではなくて、alertやprintで表示したほうがみやすいね。)


/* myscript.js */
try {
JSAN.use("Search.Fast");
} catch(e) {
throw new Error("This script requires JSAN.js" +
" and modules: Uitl" +
" (" + e.message + ")" );
}

宣言ではないので、ソースの先頭ではなくても任意の場所で(たとえループのなかでも)JSAN.useを使用できます。

JSAN.use以外にJSAN.requireもありますが、これについてはbrazilさんの記事などを参考にしてください。

●モジュール側のお約束

モジュールのファイルは正しい場所(モジュールパス)に置かなくてはなりませんが、書き方に若干のお約束(コンベンション)があります。Search.Fast-0.21(これ、架空のモジュールだけど)なら次のような感じです。


/* Search/Fast.js (Search.Fast-0.21)
*/

try {
JSAN.use("Util");
} catch(e) {
throw new Error("Search.Fast requires JSAN to be loaded");
}

// 親の名前空間オブジェクトSearchがなければ作る
if (typeof Search == 'undefined') {
Search = {};
}

// 当該の名前空間オブジェクトを生成する
Search.Fast = {};

/* Search.Fastがクラス(もどき)なら、
Search.Fast = function(mayHaveArgs) { コンストラクタコード }
のようになる。
*/

// モジュールのメタ情報:
// それほど律儀に書かなくてもいいが、VERSIONは入れよう。
Search.Fast.NAME = 'Search.Fast';
Search.Fast.VERSION = '0.21';
Search.Fast.EXPORT = ['searchFast'];
Search.Fast.EXPORT_OK = ['searchOptions'];
Search.Fast.EXPORT_TAGS = {
':all': (Search.Fast.EXPORT).concat(Search.Fast.EXPORT_OK),
':common': Search.Fast.EXPORT
};

EXPORT配列に並べた名前(記号)は、JSAN.useが自動的に大域スコープに登録します。したがって、Search.Fast.searchFastは単にsearchFastとしてもアクセスできます。EXPORT_OK配列のなかの名前は要求があれば大域スコープに登録可能です。EXPORT_TAGSは、大域スコープへの登録(export)を便利にする仕掛けです。

これ以上のことは、直接 http://openjsan.org/ にあたってみてください。

●まとめ

  1. JSANは、JavaScriptライブラリのアーカイブサイトである。
  2. JSANのモジュールシステムがJSAN.jsとして公開されている。
  3. モジュールとは、他のスクリプトから使えるJavaScriptソースファイルである。
  4. モジュールパスとは、モジュールファイルの基準ディレクトリからの相対パスのこと。
  5. モジュール名は、モジュールパスから拡張子を除き、「/」(パスセパレータ)を「.」に置き換えた名前。
  6. JSAN.useとJSAN.requireは、複数の基準ディレクトリから、モジュール名で指定したモジュールを探してロードしてくれる。
  7. モジュールを書く際に、若干のお約束ごとがある。
  8. JSANモジュールシステムは便利だから、みんな使おう。

[追記 dateTime="夕方"]: 修正した内容:
まず、Farstのrはいらない、Fastですね。
myscript.jsサンプルコードに、JSAN.use("Util") ってのがあったのですが、UtilはSearch.Fastが使うので、myscript.jsではなくて、Search/Fast.js内でJSAN.use("Util")を行うべきです。そのように直しました。[/追記]