JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティスを読み読み中。 関数の呼び出しパターンによって this の内容が変化することについてメモ。
【共通の話題】 すべての関数は呼び出されるときに2つのパラメータを自動で受け取る。 - this - arguments
このうち this が、呼び出しパターンによって値が異なる。
・メソッド呼び出しパターン 関数がオブジェクトのプロパティとして格納されている場合:メソッドとして呼び出される。 メソッドが呼び出される→thisにはそのオブジェクトが格納される。
var myObject = { value: 0, increment: function (inc) { this.value += typeof inc === 'number' ? inc : 1; } };
myObject.increment(); document.writeln( myObject.value ); // 1
myObject.increment(); document.writeln( myObject.value ); // 3
thisへのオブジェクトのセットは、呼び出しが発生したタイミングで行われる(遅延束縛と呼ばれる)。 パブリックメソッド:thisを使って自分自身のオブジェクトコンテキストにアクセスしているメソッド。
・関数呼び出しパターン 関数がオブジェクトのプロパティでない場合、「関数」として呼び出される。
var sum = add(3, 4); // 7
thisにはグローバルオブジェクトがセットされる:言語の設計としては失敗らしいこれ。 あるオブジェクトのメソッドにおいて、処理の一部を内部関数に担当させることがこのままではできないけど 回避方法が以下↓
// 上記の続きで、myObjectにdoubleメソッド追加 myObject.double = function (){ var that = this; // 値の退避 var helper = function (){ that.value = add(that.value, that.value); // ここが「this」だと、thisにはグローバルオブジェクトが入るのが無理。 }; helper(); // helperを関数として呼び出す };
myObject.double(); // doubleをメソッドとして呼び出す document.writeln(myObject.value); // 6
・コンストラクタ呼び出しパターン Javascriptはプロトタイプ継承をする言語。 つまり、オブジェクトがほかのオブジェクトから直接継承する。 クラスの概念は存在しない。
もし関数を呼び出す際、new演算子が前についていた場合、 新しいオブジェクトが生成され、thisにはその新しいオブジェクトがセットされるようになる。 そのオブジェクトは、呼び出された関数のprototypeプロパティへの隠されたリンクを持っている。
// Quoという名のコンストラクタ関数を生成。 // これはstatusプロパティを持つオブジェクトを生成する var Quo = function (string) { this.status = string; };
// get_statusというパブリックメソッドをQuoのすべてのインスタンスで利用可能にする Quo.prototype.get_status = function () { return this.status; };
// Quoのインスタンス生成 var myQuo = new Quo("confused"); document.writeln( myQuo.get_status() ); // confused
new演算子をつけて呼び出すことを前提とした関数:コンストラクタと呼ばれる。 が、コンストラクタをnew演算子を使わずに呼び出したら、たいていよくない事態が発生するが、 コンパイル時にも実行時にも警告でない⇒先頭を大文字にしてコンストラクタであるとアピることが重要。 あんまnew演算子でコンストラクタ関数呼び出す方法はお勧めできない。
・apply呼び出しパターン Javascriptは関数型オブジェクト指向言語であり、関数はメソッドを持てる。 applyメソッドで、引数を格納した配列を使って関数を呼び出せる。 さらに、applyメソッドにより、thisにセットされる値を自由に設定可能になる。 applyメソッドのパラメータは -第1引数 thisにセットしたい値 -第2引数 パラメータの配列
// 2つの数値からなる配列を作り、それらを足す var array = [3, 4]; var sum = add.apply(null, array); // sumは7
// statusというメンバを持つオブジェクトを生成する var statusObject = { status: 'A-OK' };
// statusObjectはQuo.prototypeを継承してない。 // が、statusObjectがget_statusメソッドを持ってないにもかかわらず、 // statusOjectのget_statusメソッドを呼び出すことが可能になる var status = Quo.prototype.get_status.apply( statusObject ); // 'A-OK'
|