« Twitterで開催中のエクトリーム聖火リレーをヲチする | トップページ | 今更 iPhoneで辞書を引けるよう iZisyoを作った »

2008年7月26日 (土)

JavaScriptの with文で prototype.jsの bindを省けたが

ちょこっと AJAXっぽいものを書いて遊んでいる。 なおきさんのjKeyboardを元にいじってるんだけど。

そのなかに bind methodの実装がある。 prototype.jsから持ってきた物らしいんだけど気持ち悪い:

Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat($A(arguments)));
  }
}
JavaScriptの thisは呼び出されるときまでなんになるか決まらないので、それを bindして closureのようにしてしまうためのものだ。

例えば、今の thisの barを 100ms 後に変更したくて次のように書いてはいけない:

   setTimeout(function() {this.bar = "foo"}, 100);
なぜなら function のなかの this 擬変数は、 今の contextの thisとは別物だから。 そのとき thisが何を指してるなんて不定なのだ。shit!

でも、bind methodを使うとそれが解決できる。 下のように書けば、無名関数を実行する時の thisが今の thisを同じ objectであることを保証できる:

   setTimeout((function() {this.bar = "foo"}).bind(this), 100);
でもそもそも、関数を渡すようなインタフェースになっているというのが OOPLとしてだめじゃん。 ちゃんとしたインタフェースを持った objectを渡すのが OOPLとしての王道だろ。 それを関数を渡すようなことしてるから、schemeの真似したくなって、 でも thisは static scopeに収まらないものだからこんな汚い事してる。

まったく、JavaScriptは腐った言語だ。

でも with文を使ってこれを回避できることに気がついた。

   with(this) {
     setTimeout((function() {bar = "foo"}), 100);
   }
こう書けば、 今ある thisが scope object (って呼び方でいいんだっけ) に なって、barはその member (さらにいえば associationの要素) として 解決されることになる。

static scopeのはずなのに参照すべき environmentを動的に with文で 変えてしまうというかなり変態な技だ。 こんなのができるのは JavaScriptぐらいなのではないか。

ちなみに Safari 3.0 と Firefox 3.0 で動くの確認しました。

まあ、でも geekの多い JavaScript界隈ではこんなのとうに知られているに 違いない。 それでも使われてないのは理由があるのだろうか?

とおもったらwith文は非推奨かもという議論を見つけた。 まあ、スコープを柔軟過ぎるぐらいに変更できる機能だから、 最適化が難しくなるのもむべなるかな。 まあ、とりあえず bind よりは遅くないだろうから使っておこう。

追記: ぐぐったら 既知の技法のようでした。車輪の再発見をしてしまった。

|

« Twitterで開催中のエクトリーム聖火リレーをヲチする | トップページ | 今更 iPhoneで辞書を引けるよう iZisyoを作った »

コメント

この記事へのコメントは終了しました。

トラックバック


この記事へのトラックバック一覧です: JavaScriptの with文で prototype.jsの bindを省けたが:

« Twitterで開催中のエクトリーム聖火リレーをヲチする | トップページ | 今更 iPhoneで辞書を引けるよう iZisyoを作った »