JavaScriptで名前空間

Firefox4JavaScriptと言えば

ですよね。

で、昨日辺りにJavaScript名前空間に関してTwitter上で話題に上がっていたので自分もやってみた。

コード

Firefox4でしか動かないのでご注意。

いやー、意味不ですよね。

サンプル

// 上記コードが読み込まれていること
var myNS = NS("teramako", "http://www.hatena.ne.jp/teramako/");
myNS.foo = "bar";
// => window["http://www.hatena.ne.jp/teramako/::foo"] = "bar"
alert(myNS.foo); // "bar"

NS関数は引数を元に生成したECMAScript HarmonyのProxyオブジェクトを返す。ProxyオブジェクトってのはProxy.create関数で作られ、ECMAScriptの内部関数である[[Get]]とか[[Set]]とかにテコ入れできちゃう黒魔術的なシロモノ。

E4X名前空間に関して

E4XECMAScript for XMLの略であるわけで、基本的にはXMLECMAScriptネイティブで操作しちゃおうというもの。*1
以下のような感じで直にXMLが書けちゃう。

var ns = "http://example.com";
var xml = <x xmlns={ns}><a>foo</a></x>;
xml.ns::a; // => <a xmlns="http://example.com">foo</a>

で、当然XML名前空間をサポートするわけだが、そのサポートの仕方が上記のように変数.名前空間の変数::タグ名でアクセスでき、::なんてのを書けちゃうようになるんですね〜。
問題はこの書き方は別にE4XXMLを収めた変数に限らない点。

var ns = "http://example.com";
var o = {};
o.ns::foo = "bar";

なんてことができちゃう。

まさに「名前空間」じゃないですか、というわけ。

ただ、この書き方は厄介な点があって、名前空間を収めた変数にはプロパティアクセスが入ってはいけないわけ。つまり

var NSs = { teramako: "http://www.hatena.ne.jp/teramako/" };
var o = {};
o.NSs.teramako::foo = "bar" // => ReferenceError: teramako is not defined

こういうことはできない。teramakoって変数に名前空間変数が入っていることを期待されちゃう。グローバル変数クロージャからアクセスできる変数でないといけないというわけ。
せっかく、名前空間として使えそうなのに、名前空間に使用する変数はグローバル変数的なところにいないといけないという悲しい事態なのである。

そこで、ECMAScript Harmonyの登場である。Proxyを使って内部関数の[[Get]]などを書き換えて透過的に指定した名前空間へアクセスできるようにしちゃおうというわけだ。

再びサンプルコード解説

最初のサンプルコードに戻ろう

// 上記コードが読み込まれていること
var myNS = NS("teramako", "http://www.hatena.ne.jp/teramako/");
myNS.foo = "bar";
// => window["http://www.hatena.ne.jp/teramako/::foo"] = "bar"
alert(myNS.foo); // "bar"

コード上では単にNS関数からオブジェクトを貰ってそこにプロパティをセットしてalertしているようにしか見えないが、実際にセット/ゲットしているプロパティは名前空間付きなのである。NS関数の第3引数を省略するとプロパティの設定先はthis、つまり通常であればwindowを指すことになる。省略するとグローバル変数を定義することと同等となるわけだが、心配されるプロパティ名の衝突は名前空間がしっかりしていれば回避できる。


以上、最近思いついた黒魔術でした
:wq!

*1:DOMとは違うので注意