Apr 26, 2007

All-in-One GesturesとSplit Browser

「諦めた」と宣言したそばからナンだけど、あの後もう少しがんばって、どうにか対応してみた。aioContentのリダイレクタになるオブジェクトを定義するだとかの珍妙なテクニックを駆使しまくって、やっとのことで。

AiO Gesturesのためだけに7KBくらいはコード書いた気がする……あまりにハック用コードが膨大になってきたから別ファイルに分離しちゃったよ(リンク先の半分あたりから先は全部AiO Gestures用のコード)。

aioContentの再実装?をするにあたって、AiO Gesturesのやるメソッドの上書きをリダイレクトしつつちゃんと動くようにする努力の過程で気づいたこと。prototype.jsの解説とか見てて「thisの束縛」という話をたまに目にしてたけど、それって要は、「Function.apply()なりFunction.call()なりを使って明示的にスコープを指定して関数を実行する関数」を生成してやるって事なんだろうか。つまりこういう感じ。


function MyClass(aArg) {
  this.prop = aArg
}
MyClass.prototype = {
  prop   : null,
  method : function() { alert(this.prop); }
};

var obj1 = new MyClass('obj1');
var obj2 = new MyClass('obj2');

obj2.method = function() { this.method.apply(obj1, arguments); };

obj2.method(); // "obj1"

例が悪くて意味が分からんかったので実例を示そう。Split Browserのコードの一部を示す。


window.aioContent = {
  set removeTab(val) {
    return SplitBrowser.activeBrowser.removeTab = val;
  },
  get removeTab() {
    return SplitBrowser.activeBrowser.removeTab;
  }
};

これは一番最初に書いたコード。別の箇所でaioContent = document.getElementById("content")として定義されたaioContentを置き換えて、Split BrowserのAPIで分割されたブラウズ領域でも機能するようにしようとした。AiO Gesturesの中でaioContent.removeTab = function() { ... }という感じでメソッドを置き換えている箇所について、関数オブジェクトのセット先をリダイレクトしている。

でもこれは実際には期待したとおりには動かない。aioContent.removeTab()が呼ばれた場合、SplitBrowser.activeBrowser.removeTabに格納されていた関数オブジェクトがaioContentをスコープとして実行されるので、まともに動かなくなってしまう。分かりやすく言えば、


aioContent.newMethod = SplitBrowser.activeBrowser.removeTab;
aioContent.newMethod();

という風になっているのと同じことになる。

これを解決したのが、以下のコード。

window.aioContent = {
  set removeTab(val) {
    return SplitBrowser.activeBrowser.removeTab = val;
  },
  get removeTab() {
    return this.wrappedRemoveTab;
  },
  wrappedRemoveTab : function() {
    SplitBrowser.activeBrowser.removeTab.apply(SplitBrowser.activeBrowser, arguments);
  }
};
/code>

wrappedRemoveTabは「SplitBrowser.activeBrowser.removeTabの関数オブジェクトをSplitBrowser.activeBrowserのスコープで実行する関数」だ。これをremoveTabプロパティで返してやることによって、


aioContent.newMethod = function() {
  SplitBrowser.activeBrowser.removeTab.apply(SplitBrowser.activeBrowser, arguments);
};
aioContent.newMethod();

と書いたのと同じことになって、やっと意図通り動いてくれるようになった。

数年遅れでAjaxの流れにやっと追いつけた感じです……

エントリを編集します。

wikieditish message: Ready to edit this entry.











拡張機能