JavaScriptで名前空間
Firefox4のJavaScriptと言えば
- ECMAScript 5th
- ECMAScript Harmony
- E4X
ですよね。
で、昨日辺りにJavaScriptの名前空間に関してTwitter上で話題に上がっていたので自分もやってみた。
参考
サンプル
// 上記コードが読み込まれていること 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の名前空間に関して
E4XはECMAScript for XMLの略であるわけで、基本的にはXMLをECMAScriptネイティブで操作しちゃおうというもの。*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の名前空間をサポートするわけだが、そのサポートの仕方が上記のように変数.名前空間の変数::タグ名
でアクセスでき、::
なんてのを書けちゃうようになるんですね〜。
問題はこの書き方は別にE4XのXMLを収めた変数に限らない点。
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とは違うので注意