Category script.

とりあえず似非 require を使うことに。ミニ JSAN みたいな実装にしといた。変数のリストを取得できないからこれ以外方法がなさげ。まぁ必要なオブジェクトを明示できるから悪いとはいえないんだけど。

function require(__path, __exports) {
if (!__exports) __exports = [];
var __code = (require.LOADED_FEATURES[__path]) ? require.LOADED_FEATURES[__path] : HTTP.get(__path);
(function () {
with (Global) {
eval(__code);
}
for (var __i = 0, __len = __exports.length; __i < __len; __i++) {
Global[__exports[__i]] = eval(__exports[__i]);
}
})();
require.LOADED_FEATURES[__path] = __code;
return true;
}
require.LOADED_FEATURES = {};
require("/ruby.js");
require("/xb.js",   ["document_addEventListener", "document_removeEventListener"]);
require("/dom.js",  ["getElementsByAttribute", "getElementsByClassName"]);
require("/mm.js",   ["MetadataManipulator"]);
require("/code.js", ["markupCode"]);
require("/util.js", ["MessageArea", "ProgressBar", "AccessibilityOption"]);

余計なことをしても使わないので、できるだけ短くすることにした。"Obj.prop" みたいなのも渡せるようにしたやつも書いてみたけど思ったより長くなったので却下した。

HTTP.get とか書いてるけどこれより前にそういうのを書いてある。

さっき気付いたんだけど IE で表示崩れるよね。なんか直す気力がないから放置するよ。別に読めないわけじゃないし。それにともなって MM のテストをいったん廃止

  1. トップ
  2. web
  3. site-script.js の整理
  1. トップ
  2. script
  3. site-script.js の整理

Javascript はファイル間の依存関係を一切書けない。ロードする順番は結局 script 要素の出現順、つまり HTML 依存。どう考えても気持ち悪い。のでどうにかして require もどきを作りたい。

function createXMLHttpRequest() {
return this.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
}
var __LOADED_FEATURES = {};
var Global = this; // this == window == Global
function require(lib) {
if (__LOADED_FEATURES[lib]) return false;
var req = createXMLHttpRequest();
req.open("GET", lib, false); // 同期
req.send(null);
if (req.status == 200) {
// IE ではグローバルコンテキストで実行してくれない。
Global.eval(req.responseText);
__LOADED_FEATURES[lib] = true;
} else {
throw Error("Load Error");
}
return true;
}
require("/ruby.js");
require("/mm.js");

Global という名前のオブジェクトが宣言されてなかったりするので強調をこめて宣言してある。それ以上の意味はないです。window.eval でも問題なし。見たとおり XMLHttpRequest を使って取得し、eval させてみる。

これは Firefox 1.0.7と Opera 8 ではうまくいくけど、IE 6 ではうまくいかない。

IE の場合 eval する部分のコンテキストが変わってくれない。つまり mm.js に var MottoMottoMeidosan = function () { .. } とかいう宣言があったとき、require 関数の変数オブジェクトに代入されやがるので、require のあとに new MottoMottoMeidosan() とか書くと「そんな変数宣言されてません」と怒られる。

解決法なんですが、さっぱりわかりません。誰か助けて。

なんだったら .php にでもして include() でもいいんだろうけど、これは完全に負けだよね(謎

卑怯な解決方法1を発見した。

eval(req.responseText.replace(/^var/g, ""));

インデント依存っていうか副作用がどれだけあるか不明すぎ。

JSAN つかえよバーカ。

JSAN はそもそもグローバルスコープで eval することなんて考えてない。指定したオブジェクトだけグローバルスコープに登録してる。つまり基本的に一個のファイルで一つの固まりしかロードできない。use は必要なのをグローバルスコープに登録するらしい。 DOM 定数とかをグローバルスコープに登録するなら全部 use/export の引数に書けってことなのか?

なんか素晴らしすぎて使いづらいなぁ。特定ファイルを単純にロードしたい。とくにディレクトリ構造を要求するのがなんともいえない。

疑問なんだけど JSAN は 291 行ものコードを最初から書いておけっていうだろうか?

script 要素二つ書けっていうんだったら目的が違うな。モジュールシステムが欲しいんじゃないし。

続きを書いた。

  1. トップ
  2. web
  3. Javascript で require もどき・eval の実行コンテキスト
  1. トップ
  2. script
  3. Javascript で require もどき・eval の実行コンテキスト

ECMAScript でイテレータ なんてのを書いたことがあったけど、これ、each の中で break ができないのでちょっと気持ち悪い。ちまたで時々聞く ruby.js はどうやって解決しているんだろう?って思ってみてみたけど、特に何もしてなかった。つーか ruby.js 見つけるのが割りとめんどかったよ。

いろいろ考えたけどとりあえず動いたのをメモっとく。

function Array_each(func) {
try {
var context = {
escape : function () {
throw "break";
}
}
for (var i = 0, len = this.length; i < len; i++) {
func.apply(context, [this[i]]);
}
} catch (e) {
if (e != "break") throw e;
}
return this;
}
Array.prototype.each = Array_each;
[1, 2, 3].each(function (i) {
if (i > 1) this.escape();
Debug.dump(this);
});

Function.prototype.apply() の第一引数は関数の中での this なのでそれにメソッド設定してみる。ループから抜けるのに try - catch。投げるオブジェクトは "break" という文字列。"break" 以外の場合は例外を投げなおす。

ほんとはコンテキストの変数オブジェクトに直接関数を突っ込めるのがいいんだけど、ECMAScript では変数オブジェクトにアクセス方法はない (Activation オブジェクトは変数の実体化を目的とした変数オブジェクトとして使用される。Activation オブジェクトは純粋に仕様のメカニズムである。 Activation オブジェクトへのアクセスは ECMAScript プログラムには不可能である。 メンバ (変数) にはもちろんアクセスできるから eval つかえばいいけどスマートとは思えない。) のでこんな感じ。Global (window) は使いたくないし……

あとは eval(func.toString().replace(/\{/, "\{ var escape = function () { .. };")) とか?

  1. トップ
  2. prog
  3. Ruby's eash on ECMAScript
  1. トップ
  2. script
  3. Ruby's eash on ECMAScript