RequireJS moduleについて

RequireJSって何?

公式サイト
RequireJS
スライド
jQueryRequireJS.pdf
日本語記事だとこの辺?
http://zudolab.net/blog/?p=451


要はJavaScriptの依存性解決をしてくれるライブラリで、こんな感じで使えます。

require(
  [
    'lib/a'
    ,'lib/b'
    ,'lib/c'
  ],
  function(){
    // lib/a.js,lib/b.js,lib/c.jsが読み込まれていることが保証されているcallback
    require(
    [
      'lib/d' // lib/a.jsに依存しているライブラリ
      ,'lib/f'// lib/b.jsに依存しているライブラリ
    ],
    function(){
      // lib/a.js,lib/b.js,lib/c.js,lib/d.js,lib/e.jsが読み込まれていることが保証されているcallback
    });
  }
);

callbackの中でならライブラリが読み込まれてる前提でアプリケーションコードが書けるし、
callbackの中でrequire関数を呼べば、あるライブラリに依存する別のライブラリに依存する
アプリケーションとかも書けちゃいますよ、という感じ。
と言っても、これはかなり単純な使い方で、アプリケーションコードの規模が
小さい場合でないと実用的ではないと思う。

RequireJS moduleって何?

そこで、moduleですよ。

define({})

例えば、こんな内容のhoge.jsが配置されているとする。
このhoge.jsがhoge module。

define({
  hoge : 5
  ,fuga : 'piyo'
});

requireの第一引数に依存しているmodule名の配列を渡すと、
その順にcallback関数に渡される。

require(['hoge'],function(hoge){
  console.log(hoge.hoge); // 5
  console.log(hoge.fuga); // 'piyo'
});
define(function(){})

moduleはdefine関数にオブジェクトを渡すだけでなく、
オブジェクトを返す関数を渡す形でも定義できる。
これはfoo.js(foo module)

define(function(){
  return {
    bar : 6
    ,baz : 7
  }
});

先程のhoge moduleとあわせて使うと

require(['hoge','foo'],function(hoge,foo){
  console.log(foo.bar); // 6
  console.log(hoge.fuga); // 'piyo'
});

依存関係の連鎖

RequireJS moduleではdefine関数の第一引数にmodule名の配列を渡すことで、
moduleが依存するmoduleを指定することができる。
hage.js(hage module)

define(['hoge','foo'],function(hoge,foo){
  return {
    bar : foo.bar
    ,baz : foo.baz
    ,hoge : hoge.hoge
    ,fuga : hoge.fuga
  }
});

下記のrequire(['hage'])ではhage moduleしか指定していないが、
依存先のhoge,fooも読み込まれている。
hage moduleを使いたい人はhage moduleが何に依存しているか知っている必要はない。

require(['hage'],function(hage){
  console.log(hage.bar); // 6
  console.log(hage.fuga); // 'piyo'
})

require(['hage'])を呼んだ場合、以下のような順で処理が実行される。

  1. hage.jsが読み込まれる
  2. hoge.jsが読み込まれる(hoge moduleが定義される)
  3. foo.jsが読み込まれる
  4. foo.jsでdefine関数に渡しているcallbackが実行され、戻り値がfoo moduleとして定義される
  5. hage.jsでdefine関数に渡しているcallbackが(hoge,foo moduleを使用して)実行され、戻り値がhage moduleとして定義される
  6. require(['hage'])のcallbackにhage moduleが渡され呼び出される

define関数のcallbackはmoduleの初期化用であって、
そのmoduleが依存moduleとして初めて呼ばれた時だけ実行される。


この依存関係の連鎖は何段階でもできる。

関数・class

今までの例ではmoduleとして連想配列しか定義していなかったが、
JavaScriptで扱えるすべての値がmoduleとして定義できる。
例えばこんなvery-useful-function.jsがあれば、

define(function(){
  function veryUsefulFunction(){
    /*
     * 何かすごい実装
     */
  }
  return veryUsefulFunction;
});

これを使いたい人は、

require(['very-useful-function'],function(veryUsefulFunction){
  /*
   * 何か自前のコード
   */
  veryUsefulFunction();
});

とか

define(['very-useful-function'],function(fun){
  /*
   * 何か自前のコード
   */
  fun();
  return module;
});

とか書いてやればいいし、


こんなvery-useful-class.jsがあれば、

define(function(){
  function VeryUsefulClass(){
  }
  VeryUsefulClass.prototype.benri = function(){
    /*
     * 何かすごい実装
     */
  };
  return VeryUsefulClass;
});

こんな風に書いてやればいい

require(['very-useful-class'],function(Useful){
  var hogehoge = new Useful();
  hogehoge.benri();
});


それぞれRequireJSのmoduleとして関数やclassを提供しているが、
グローバルは全く汚染していない。

まとめ

依存関係の連鎖と関数・classの提供などを組み合わせて、
アプリケーションコードを小分けにしていけば、
JavaScriptアプリケーションのコード規模が大きくなっても
スパゲッティ化を逃れられるのではないかと期待している。
もそっと深いところを把握した上で、java-ja.js#2で発表しようと思う。