使ってみよう!「Closure Tools」 #5 ~名前空間の省略~

2011.10.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

毎度お世話になっております。クラスメソッドの稲毛です。

『使ってみよう!「Closure Tools」』の第五回は「名前空間の省略」についてです。

Closure Libraryのライブラリ群は、グローバル変数領域にオブジェクトの入れ子構造で管理されています。例えば、「goog.ui.Component」クラスであればグローバル変数「goog」の「ui」内に「Component」として関数(クラス)が定義されます。そこで気になるのは「goog.ui」や「goog.dom」などの名前空間部分を毎回記述しなければならない点です。先ほどの例でいえば、「var comp = new goog.ui.Component();」というより「var comp = new Component();」と記述したいですよね。そのような時には「goog.scope」関数を利用して「名前空間の省略」を行うことが出来ます。

goog.scope関数

前回作成した「App」クラスで試してみます。

App.js(変更前)

goog.provide('example.App');
 
goog.require('goog.dom');
goog.require('example.Hello');
 
example.App = function()
{
  var hello = new example.Hello();
  hello.say(goog.dom.getElement('hello'));
};
 
new example.App();

「Hello」クラスのインスタンス生成時に名前空間「example」を省略する為には以下のように記述します。

App.js(変更後)

goog.provide('example.App');
 
goog.require('goog.dom');
goog.require('example.Hello');
 
goog.scope(function() {
  var Hello = example.Hello;

  example.App = function()
  {
    var hello = new Hello();
    hello.say(goog.dom.getElement('hello'));
  };

  new example.App();
});

「App」クラスの定義以降を「goog.scope」関数の引数としてクロージャで包括し渡すようにします。(6行目から16行目)
スコープの中で「example.Hello」関数のエイリアスとなる一時変数「Hello」を定義します。(7行目)
エイリアスを定義したことで、「example.Hello」ではなく「Hello」によってインスタンス生成が可能となります。(11行目)

エイリアス「Hello」はクロージャ内の閉じたスコープ内に定義されている為、グローバル変数に影響を与えることはありません。※「goog.scope」を用いないでエイリアスを指定すると、グローバル変数に「Hello」が定義されてしまう為、同名の変数名が用いられていると影響を及ぼしてしまいます。

同様にClosure Libraryの関数もエイリアスを作ることができます。

goog.provide('example.App');
 
goog.require('goog.dom');
goog.require('example.Hello');
 
goog.scope(function() {
  var dom = goog.dom;
  var Hello = example.Hello;

  example.App = function()
  {
    var hello = new Hello();
    hello.say(dom.getElement('hello'));
  };

  new example.App();
});

「goog.dom」のエイリアスを「dom」として定義してみました。「goog.dom.getElement」関数を「dom.getElement」として利用できます。※もちろん「goog.dom.getElement」を「$」等と定義することも可能です。

このように「goog.scope」関数が提供するクロージャ内にエイリアスを定義することで、行数こそ増えるもののコードの可読性を向上させることが出来ます。

「goog.scope」を用いた場合のコードスタイルは以下のように提案されています。

  • 最後のgoog.requireとgoog.scope宣言の間にはブランク行を1行用意する。
  • goog.scopeはファイル全体(goog.provideやgoog.requireは除く。)をラップする。ラップされた内容部分は2文字分のスペースでインデントする。
  • エイリアスはアルファベット順に定義する。
  • ユニークな定数名(名前空間やコンストラクタのような)はエイリアスを用意するべきである。 スコープ内のエイリアスは、グローバル名の最後のプロパティ名(「goog.dom」→「dom」「goog.ui.Component」→「Component」)とする。

scopify.py

実は既に実装されているスクリプトに「goog.scope」を適用するツールがClsoure Libraryには用意されています。それがPythonスクリプトの「scopify.py」です。このスクリプトは、引数として指定したパス内のJavaScriptファイルを再帰的に探索し名前空間の省略が可能な場合、「goog.scope」関数を挿入しエイリアスを自動的に定義してくれます。

python path/to/closure/bin/scopify.py path/to/script

ただしエイリアス化対象はクラスだけのようです。名前空間のエイリアス化(「goog.dom」→「dom」)は自身で記述する必要がありますのでご注意ください。

まとめ

サンプルの名前空間では入れ子が少ない為にあまり恩恵は感じられないかもしれませんが、より深く複雑な構造の名前空間でクラス群を管理する場合などでは非常に重宝するかと思います。勿論この記述法は「Closure Compiler」を利用したJavaScriptファイルの統合および最適化には影響しないので積極的に利用することができます。