JavaScriptにおける「this」が指し示すもの
JavaScriptにおける「this」は、「呼び出し元」で指し示すものが変わってくる。
基本的に以下の4種類のパターンが存在する。
- 関数呼び出し(グローバルオブジェクト)のパターン
- メソッド呼び出し(所属オブジェクト)のパターン
- コンストラクタ呼び出し(インスタンス自身)のパターン
- apply/call呼び出し(強制変更)のパターン
関数呼び出し(グローバルオブジェクト)のパターン
1 2 3 4 5 6 7 8 | // 関数定義 function func() { this.value= "foo"; console.log(this); console.log(this.value); } // 関数呼び出し(thisはグローバルオブジェクトを指す) func(); |
この場合、「this」は「グローバルオブジェクト」を指す。
つまり、「value」は「グローバル変数」になる。
メソッド呼び出し(所属オブジェクト)のパターン
1 2 3 4 5 6 7 8 9 10 | // オブジェクト定義 var myObject = { value: "bar", method: function() { console.log(this); console.log(this.value); } } // メソッド呼び出し(thisは所属オブジェクトを指す) myObject.method(); |
この場合、「this」はメソッド「method」が所属しているオブジェクトである「myObject」を指す。
因みに以下のようにメソッド呼び出しの中で関数呼び出しをした場合でも関数内の「this」はグローバルを指してしまう為、関数呼び出し時の「this.value」は「undefind」となる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // オブジェクト定義 var myObject = { value: "bar", method: function() { console.log(this); console.log(this.value); // 関数定義 function method() { console.log(this); console.log(this.value); } // 関数呼び出し method(); } }; // メソッド呼び出し myObject.method(); |
尚、「this」を別の変数(下記の例では「self」)に格納しておくことで、この問題を解決することができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // オブジェクト定義 var myObject = { value: "bar", method: function() { // thisを別の変数に格納 var self = this; console.log(this); console.log(self); console.log(self.value); // 関数定義 function method() { console.log(this); console.log(self); console.log(self.value); } // 関数呼び出し method(); } }; // メソッド呼び出し myObject.method(); |
コンストラクタ呼び出し(インスタンス自身)のパターン
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 関数定義(コンストラクタ呼び出しを期待) function MyObject(value) { this.value = value; this.increment = function() { console.log(this); this.value++; }; } // インスタンス生成 var myObject = new MyObject(0); // インスタンス生成時の値(0)が出力される console.log(myObject.value); // インスタンスのメソッド呼出し myObject.increment(); // インスタンスのメソッド実行後の値(1)が出力される console.log(myObject.value); // インスタンスのメソッド呼出し myObject.increment(); // インスタンスのメソッド実行後の値(2)が出力される console.log(myObject.value); |
この場合、関数にnew演算子をつけてコンストラクタ呼び出しにてインスタンスを生成している為、「this」は生成されるインスタンス自身となる。
尚、new演算子をつけなかった場合、つまり関数呼び出しとなった場合は、「value」、「increment」共にグローバル変数として定義される。
※慣例として関数をコンストラクタ呼び出しで利用する場合、他の関数と区別しやすくする目的で関数名の先頭を大文字とする。
apply/call呼び出し(強制変更)のパターン
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // オブジェクト定義 var myObject = { value: "foo", method: function() { console.log(this); console.log(this.value); } }; var newObject = { value: "bar" }; // メソッド呼び出し myObject.method(); // thisの指すオブジェクトを差し替えてメソッド呼び出し myObject.method.apply(newObject); myObject.method.call(newObject); |
この場合、「this」の指すオブジェクトを「newObject」に差し替えてメソッドが呼び出される為、「newObject」の「value」の値が出力される。
「apply/call」を利用すると「強制的にthisの差し替え」ができる。
「apply/call」の第一引数は、「this」に指定したいオブジェクトになる。
尚、「apply」と「call」の違いは、第二引数の指定の仕方になる。
1 2 3 4 5 6 7 8 9 10 11 12 13 | // オブジェクト定義 var myObject = { method: function(value1, value2) { console.log(this); console.log(this.value + value1 + value2); } }; var newObject = { value: "foo" }; // オブジェクトを差し替えてメソッド呼び出し myObject.method.apply(newObject, ["bar", "baz"]); myObject.method.call(newObject, "bar", "baz"); |
出力結果は、「apply」と「call」共に同じになる。
「apply」は第二引数に配列をとり、配列の中身が引数として渡される。
「call」は、第二引数以降がそのままの形で渡される。