変なJavaScript (+ E4X)
最近、E4X の勉強をしている(仕様はStandard ECMA-357ね)
ご存知の通り(?) Mozilla の JavaScript *1には E4X が使えるのだが、いろいろ罠チックなものがあったり、ECMAScriptの仕様を壊してしまっていたりと面白い(ぉ
const XHTML = new Namespace("xhtml", "http://www.w3.org/1999/xhtml"), LANG_JA = new Namespace("ja", "http://example.com/ja"), LANG_EN = new Namespace("en", "http://example.com/en"); default xml namespace = XHTML; var xml = <root xmlns:ja={LANG_JA} xmlns:en={LANG_EN}> <ja:item id="1">hoge</ja:item> <ja:item id="2">piyo</ja:item> <en:item id="3">foo</en:item> <en:item id="4">bar</en:item> </root>
属性値取得
hoge
がある id 属性を得るならば
xml.*[0].@id
などと、@
を付ければ良い。
名前空間
また、ja:item
のノードリストを得たいならば
xml.LANG_JA::item
ワイルドカードと組み合わせて
xml.LANG_JA::*
とすれば、要素名に関係なくリストを得られる
xml.*::*
なんてことも可能。
驚きである。なんと ECMAScript では.
によるプロパティ取得は IdentifierNameとして定義されているが、そこに*
や@
は含まれてはいけないルールとなっている。普通なら構文エラーだ。
何かが出来る予感である。
var o = {}; o.* = "hoge"; alert(o.*); o.@a = 10; alert(o.@a); o.*::* = 30; alert(o.*::*);
みんなキッチリ動いてくれる。
また、名前空間だが、別にNamespaceのオブジェクトでなくて、文字列でも構わない。
var ns = "hoge"; var o = { "hoge::piyo": 20 } alert(o.ns::piyo);
あと、これも多分ワイルドカードからみだと思う。
0.*.0
はChromeで実行しても0
が返ってきて何が何だか分かららいがw0. === 0.0 // つまり 0 .0 === 0.0 // つまり 0 0.*.0 === 0 * 0なんだ!
あと、
xml.LANG_JA:item === xml.LANG_JA:item // false
である。XMLオブジェクトのプロパティを取得するとき、内部ではまず空のXMLListを生成して、適合するプロパティを取得し、リストに追加して返すため、別インスタンスとなる。これは仕様にも書かれているとおりなので、そういうものなのだろう。
そのため、xml.hogehoge
など存在しないプロパティを取得すると、空のXMLListが返る。undefined
は返らないので注意。
この仕様、なのか実装なのか分からないけど、XML.prototype
のプロパティもそうで、
typeof XML.prototype.length // "xml"
となる。じゃあ、どうやってプロトタイプ拡張するんだ? っていうと、Mozillaの拡張でfunction名前空間がある。
XML.prototype.function::unco = function() alert("UNCO"); xml.unco() // "UNCO"
え? function
なんて長いコード書けない? じゃあ
var f = new Namespace("function", "@mozilla.org/js/function"); XML.prototype.f::hoge = function() alert("HOGE") xml.hoge() // "HOGE"
ってすれば良いと思うよ!
追記
また、おかしなことをしてしまった。
default xml namespace = new Namespace("function","@mozilla.org/js/function"); var x = <root> <length>hoge</length> </root>; /* <root xmlns="@mozilla.org/js/function"> <length>hoge</length> </root> */ x.length /* function length() { [native code] } */
length
のノード取れないwww
まぁx.*[0]
とかやれば取れるけど。
*1:version 1.6から