JavaScript 〜 名前空間内での変数の衝突を避ける

前回のエントリーで、グローバル変数の衝突を避けるために、名前空間を使用するパターンを紹介した。
これで、グローバル変数の衝突は避けられるようになったとしても、名前空間内で変数が衝突する可能性もある。

結局、変数を定義する際に、その変数が既に定義されていないのかをチェックするのがベストということになる。これは、グローバル変数として名前空間を定義する場合にも当てはまる。

例えば、下記コードでは、名前空間MYAPPを定義する前に既に定義されていないのかを調べてから定義している。

//MYAPPが未定義の場合に定義する
if(typeof MYAPP === "undefined"){
   var MYAPP = {};
};

これは、下記のようにシンプルに書いても良い。

var MYAPP = MYAPP || {};

これで、1つの変数を定義する際のチェックの方法はわかったが、このままではネストしたオブジェクトを定義する場合にネストの数だけチェックが必要になり大変だ。

例えば、MYAPP.modules.module1というオブジェクトを定義する場合には、下記のようにチェックが3回必要となる。

//MYAPPの存在チェック
var MYAPP = MYAPP || {};

//MYAPP.modulesの存在チェック
MYAPP.modules = MYAPP.modules || {};

//MYAPP.modules.module1の存在チェック
MYAPP.modules.module1 = MYAPP.modules.module1 || {};

3回チェックを行うのは大変であり、チェックを忘れてしまう可能性も大きくなる。
MYAPP.modulesが{myappModules:"defined"}という定義だった場合、下記のようにチェックを怠ればMYAPP.modulesが上書きされてしまう。

//MYAPPの存在チェック
var MYAPP = MYAPP || {};
var MYAPP.modules = {myappModules:"defined"};

//MYAPP.modulesの存在チェックを怠る
//MYAPP.modules = MYAPP.modules || {};
var MYAPP.modules;

//MYAPP.modules.module1の存在チェック
MYAPP.modules.module1 = MYAPP.modules.module1 || {};

//未定義値となってしまう
alert(MYAPP.modules.myappModules);

このようなチェックの回数を減らすためには、汎用の名前空間作成関数を用意すれば良い。
自前で作成してもよいが、ライブラリを利用するのも1つの手だ。
例えば、Yahooが提供しているYUIライブラリにもnamespaceメソッドが提供されている。
YUIでは、YUIという名前のグローバル変数を定義しており、それが名前空間となる。
YUIを利用して、YUI名前空間にMYAPPを定義した例が下記である。

	YUI.MYAPP = YUI.MYAPP || {};
	YUI.MYAPP.modules = YUI.MYAPP.modules || {};
	YUI.MYAPP.modules.module1 = YUI.MYAPP.modules.module1 || {nested:"yeah"}
    //yeahと表示  
    alert(YUI.MYAPP.modules.module1.nested);

さらに、YUIに定義されている名前空間の存在をチェックしてオブジェクトを返す関数namespaceを使用すれば下記のように簡潔に書ける。

YUI.namespace("YUI.MYAPP.modules.module1").nested = "yeah";
//yeahと表示
alert(YUI.namespace("MYAPP.modules.module1").nested);