前回まではレキシカル環境とクロージャについてお話ししてきました。
今回はfunctionとarrow関数でのthisの挙動の変化について書いていこうかと思います。
今回はカイージくんは登場しません。予めご了承ください。
⇩カイージくんとのお勉強はこちらをご覧ください⇩
本題:thisとは
this
は関数やメソッドがどのオブジェクトから呼び出されたかを示します。
通常の関数では、this
は関数が呼び出されたコンテキストによって決まります。例えば、オブジェクトのメソッドとして呼び出された場合、this
はそのオブジェクトを指します。
const obj = {
value: 42,
regularMethod: function() {
console.log(this.value); // this は obj を指す
}
};
obj.regularMethod(); // 42
非strict モードでは、グローバルスコープでthis
を使用すると、グローバルオブジェクト(ブラウザ環境では window)を指しますが、use strict
モードではthis
はundefined
になります。
また、通常の関数として呼び出され時、以下のようになります。
const obj = {
value: 42,
method: function() {
const innerFunction = function() {
console.log(this.value);
};
innerFunction();
}
};
obj.method(); // グローバルオブジェクト(非 strict モード)、または `undefined`(strict モード)
同様に、上記の例では、innerFunction
は通常の関数として呼び出されるため、this
はグローバルオブジェクトを指します(非 strict
モードの場合)。strict
モードでは undefined
になります。
レキシカル環境は関数がどこで定義されたかによって決まるスコープチェーンである一方、アロー関数を使用した際はthis
自体はレキシカル環境(スコープチェーン)を作りません。
以下のコードを見てみましょう。
const obj = {
value: 42,
method: function() {
const innerFunction = () => {
console.log(this.value); // ここでの `this` は外側のスコープから継承される
};
innerFunction();
}
};
obj.method(); // 42
アロー関数を使用した場合、this
は外側のスコープ(この場合は method
内のスコープ)から継承され、obj
を指します。
上記のコードを全てアロー関数で書いた場合はどうなるでしょうか。
const obj = {
value: 42,
method: () => {
const arrowFunction = () => {
console.log(this.value); // ここでの `this` はグローバルオブジェクト(非 strict モード)や `undefined`(strict モード)を指す
};
arrowFunction();
}
};
obj.method(); // undefined (strict モードの場合) または グローバルオブジェクトの `value` (非 strict モードの場合)
CallBck関数との兼ね合い
通常の関数をコールバックとして使用すると、そのthis
は呼び出し時のコンテキストに依存します。
以下で実行してみましょう。
const obj = {
value: 42,
method: function(callback) {
callback();
}
};
function regularFunction() {
console.log(this.value);
}
obj.method(regularFunction); // undefined
regularFunction
のthis
はグローバルオブジェクトを指すので、残念ながら期待通りの結果にはなりませんでした。
ここで、アロー関数は定義されたときのthis
を継承するので、コールバックとして使用してみましょう。
const obj = {
value: 42,
method: function(callback) {
callback();
}
};
const arrowFunction = () => {
console.log(this.value);
};
obj.method(arrowFunction); // undefined
しかし、arrowFunction
が定義されたときのスコープにvalue
が存在しないため、undefined
になります。
そこで、正しい this を指すようにするためには、アロー関数をオブジェクトのメソッド内で定義してみましょう。
const obj = {
value: 42,
method: function() {
const arrowFunction = () => {
console.log(this.value); // this は obj を指す
};
arrowFunction();
}
};
obj.method(); // 42
無事、辿り着けましたね。
もし、通常の関数でもコールバックで同じようにthisを求めたい時はどうすればいいのでしょうか。
ここではbind
メソッドを使って明示的に繋げる方法をご紹介します。
bind
メソッドは、定義された関数に対して、thisを代入できるメソッドです。また、その名の通り関数などをbind(紐づけ)することができます。
const obj = {
value: 42,
method: function(callback) {
callback();
}
};
function regularFunction() {
console.log(this.value);
}
obj.method(regularFunction.bind(obj)); // 42
時間があれば、わかりやすい図とかも用意するかもしれません。