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 () { .. };"))
とか?