XMLHttpRequest を使ったスクリプトのロードは意外と面倒
- XMLHttpRequest を使ったスクリプトのロードを自分で書いてみた
- JSAN を読んでみた
- JSAN よくできてる
という話。
- XMLHttpRequest で取得できるのは「データ」である
- 「データ」を「スクリプト」にするためには eval() する必要がある
- eval() した結果できるコードは eval() された場所のスコープに制限される
つまり、例えば
// スクリプトをロードするつもりの関数
function load( url ) {
// XMLHttpRequest で同期通信でコードを取得する処理
// 取得に成功したコードを eval()
}
load( hogege );
// ここでエラー
function_defined_external();
ということ。function_defined_external() という関数が load() で読み込んだスクリプトの中で定義されていても、それは load() の中で eval() したら load() の中でしかコードとして存在できないわけです。
これはこうなっていれば ok です。
function load( url ) {
..
// 読み込んだデータをそのまま返す
return xhr.responseText;
}
var script = load( url );
if ( script ) {
eval( script );
}
// これは実行可能
function_defined_external();
// (ちゃんとライブラリをロードできていれば)
- 読み込む関数は読み込みしかしない
- 返って来たテキストデータを「自分の意思で」「動かしたいスコープで」 eval() する
という二段階の操作になっちゃうけども、こうすれば問題なく使える。二段階はどうしてもイヤだっていうんでなければこの方法がいちばん手軽だと思う。1
しかし JSAN はこういうかっこ悪いことはしていない。use(), require(), export() が自在に活躍してくれる。これは、
- オブジェクトをちゃんと活用しているから
- 命名規則を設けているから
まぁ簡単に言えば JSAN.use() の中で eval() してできあがったオブジェクトのプロパティを Global なオブジェクトのプロパティにコピーすれば使えますよということ。これは prototype.js とか最近のモダンなライブラリがみんなやってる「プロパティコピペしまくりの術」の応用なわけですな。うまいこと考えたなぁ。
ただ、逆に言うと
ちゃんと JavaScript でオブジェクトを扱える人でないと JSAN を利用してロードするライブラリを書けない
ということです。
つかまず一発勝負関数とかあるわけですよ、やっぱり。すべてのコードが名前空間やオブジェクトを意識して作られた整然としたコードにはなっていないわけですし、最近の便利なフレームワークも別に JSAN の枠組みには則っていないわけです。
そういうときは上の二段階方式が扱いやすくていいな、と思っています。先の記事で書いた require() は bookmarklet など他ドメインのスクリプトをロードしたいときに、XMLHttpRequest を利用した load() は同一ドメインの無作法なライブラリ(笑)や、そのまま script 要素に書いて読み込むと MacIE などの古いブラウザでコンパイルエラーが起きてしまうライブラリ2のロードに利用していったらいいかなと思っています。