2006-11-07

XMLHttpRequest を使ったスクリプトのロードは意外と面倒

  1. XMLHttpRequest を使ったスクリプトのロードを自分で書いてみた
  2. JSAN を読んでみた
  3. 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のロードに利用していったらいいかなと思っています。

  1. with を使って window オブジェクトのスコープで eval() するって方法もあるけど。 

  2. prototype.js も JSAN もみーーーーーんなそうじゃ! 

text/javascript って obsolete なんだ

RFC4329 - Scripting Media Types

application/javascript
application/ecmascript

にしろってか。ブラウザの対応とかどうなってんだろ。

まぁ JSON が application/json になったんだから、その時点で気づけってんだよな。

なんかネタかぶりしてるけど、ぼかぁたまたま IANA Media Types を見に行っただけですよ。

というかチェックしてるはずの神崎さんのメモをなんとなくスルーしてる段階でアンテナ感度が鈍いっつー話なんだよなぁ。あ、今 Camino 1.0.3 でチェック用のボタンを押してみたら全部動いた。てことはこの方法でちょっとでも古いブラウザを根こそぎ排除できるかも(笑)

と思ったら IE7 はダメらしい。おいこら。

※ まったく本題じゃないんだけど、リンク先のページを改めて見て気がついた。Camino でも Gecko 風のボタンが表示されている。これは Safari 1.3 でも同じ。input type="button" の場合はちゃんと Aqua なボタンになるのに、button 要素の場合は昔ながらのボタンぽい表示になる。Opera 9.0.2 ではどちらも Aqua 風になる。

About

例によって個人のなんちゃらです