X-0029 元々のプロパティやメソッドを呼び出す - Components.lookupMethod()の使い方

Components.lookupMethod()を使うと、他の拡張機能などが改変する前の元々のプロパティ・メソッドを簡単に参照できます。普通のスクリプティングではあまり必要のない知識だとは思いますが、XULアプリケーションの拡張機能を作る場合は、ユーザーが他にどんな拡張機能を導入しているか分かりませんので、知っておいて損はないでしょう。

例えば、window.open()というメソッドの動作を変えるために以下のようなコードを書いたとします。

var original_open = window.open;
window.open = function(aURI, aName, aFlag) {
	/* 何らかの処理 */
	original_open(aURI, aName, aFlag);
};

この時、改変される前のwindow.open()を呼び出すためには、その内容を参照しているoriginal_open()という関数オブジェクトを使う必要があります。しかし、「original_open」という名前を知っているのはこのコードを書いた自分だけですから、これでは他の人は元々のwindow.open()を呼び出せません

もう一つ例を示しましょう。トップレベルのウィンドウを取得するためにwindow.topを参照したとします。ところが、自分の導入している他の人が作った拡張機能の中にこんなコードがあったらどうなるでしょうか?

var top    = "先頭";
var bottom = "末尾";
...

グローバル変数はwindowオブジェクトの同名のプロパティとして扱われますので、当然、window.topが示すものは文字列"先頭"となります。すると、window.topがトップレベルのウィンドウオブジェクトであることを前提に書かれたコードはエラーになってしまいます。

このようにもう参照できなくなってしまった元々のメソッドやプロパティを参照するための方法、それがComponents.lookupMethod()です。

例えば先の例でいう「元々のwindow.open()を呼び出す」場合、以下のようにします。

var original_open = Components.lookupMethod(window, 'open');
var newWin = original_open(aURI, aName, aFlag);

Components.lookupMethod()は、第一引数として渡したオブジェクトが第二引数で渡した名前のメソッドを持っている場合に、それを関数オブジェクトとして返します。

メソッドの場合は前述の通りでよいのですが、window.topのようなプロパティの場合はこれでは駄目で、単純に Components.lookupMethod(window, 'top') としただけでは関数オブジェクトしか取得できません。この場合、関数オブジェクトのcall()メソッドを使う必要があります。

var topWinGetter = Components.lookupMethod(window, 'top');
var topWin = topWinGetter.call(window);

// 通常のメソッドについても同じ書き方は可能(あまり意味はないですが)
var newWinFunc = Components.lookupMethod(window, 'open');
var newWin = newWinFunc.call(window, aURI, aName, aFlag);

call()メソッドは、その関数オブジェクトの内容を実行する際の「実行コンテキスト」、つまり、関数オブジェクトの中で参照する this の参照先を明示的に指定するものです。第一引数に実行コンテキストとなるオブジェクトを渡し、第二引数から先はその関数が本来取るはずの引数を渡します。この例では、Components.lookupMethod(window, 'top')の返り値の関数オブジェクトはゲッター(そのプロパティを参照した時に自動的に実行される特殊なメソッド)なので、こうして自分でcall()してやる必要があるわけです。

参考資料:Effective JavaScript > プロパティとメソッド