JavaScriptを使った開発をしているとthisは必ず目にすると言っても過言ではない程に使用されます。しかし、thisは挙動がかなり特殊な為、理解せず何となくで使っているとこのthisは何を参照しているのだろう?と混乱してしまいます。
そこで本記事では、JavaScriptのthisの使い方について詳しく解説しています。コピーされた時の挙動やコールバック関数時の挙動なども解説しているのでご参考ください。
- 誰でも分かるように嚙み砕いて説明してくれる
- アニメーションの知識が深く学べる
- 1つのWebサイトを作りながら学べる
Amazon Kindle Unlimitedに登録すると、月額980円で読み放題だからオススメだよ!
初回30日間は無料だから、まだ登録したことのない人はぜひ試してみてね!
変数名/関数名にもう悩まない!
- 美しいコードが書けるが自然と書けるようになる
- 他の開発者が理解しやすいコードになる
thisとは何か?
MDNに掲載されているthisについての説明は以下のとおりです。
関数の
this
キーワード は、JavaScript ではほかの言語と少々異なる動作をします。また、strict モードであるかどうかでも違いがあります。ほとんどの場合、
引用元:MDNthis
の値はどのように関数が呼ばれたかによって決定されます (実行時結合)。これは実行時に代入によって設定することはできず、関数が呼び出されるたびに異なる可能性があります。ES5 ではbind()
メソッドが導入され、関数がどのように呼ばれたかに関係なく `this` の値を設定するすることができるようになり、ES2015 では、自身ではthis
の結び付けを行わないアロー関数が導入されました (これは包含する構文上のコンテキストのthis
の値を保持します)。
MDNの解説で一番重要なポイントは「thisの値はどのように関数が呼ばれたかによって決定されます」です。
分かりやすいようにサンプルコードで確認していきましょう。
const infoObj = {
name: "John",
sayName: function () {
console.log(`私の名前は${infoObj.name}です`);
},
};
infoObj.sayName();
オブジェクト内にあるnameプロパティをsayNameプロパティに定義されている関数で使用したい場合、infoObj.nameで参照することができますが、このinfoObjはthisに置き換えることができます。
console.log(`私の名前は${infoObj.name}です`); // -> 私の名前はJohnです
console.log(`私の名前は${this.name}です`); // -> 私の名前はJohnです
何故このような結果になるのか?その答えinfoObj内でthisを使用した場合、thisは呼び出し元のオブジェクト(infoObj)への参照を保持するキーワードになるからです。
それじゃあ、「infoObj.sayName();」のinfoObjもthisで代用できるの?と思いますよね。しかしこのinfoObjはthisで代用することができません。
その理由は、infoObj内ではないからです。もし呼び出し元オブジェクトが存在しない状況でthisを使用した場合、そのthisはグローバルオブジェクトを参照します。
infoObj.sayName(); // -> infoObjを参照する
this.sayName(); // -> グローバルオブジェクトを参照する
コピーされた時のthisの挙動
続いて、オブジェクト内の関数をコピーした時のthisの挙動を確認してみましょう。先ほどのサンプルコードを使います。
const infoObj = {
name: "John",
sayName: function () {
console.log(`私の名前は${this.name}です`);
},
};
infoObj.sayName(); // -> 私の名前はJohnです
const copyObj = infoObj.sayName;
copyObj(); // -> 私の名前はです
実行してみると、コピーした関数を実行した時にオブジェクト内のnameプロパティが参照できておらず、「私の名前はです」とおかしな出力がされていることが確認できます。
この原因は、thisの呼び出し元のオブジェクトがinfoObjからcopyObjに変更されたからです。nameプロパティはinfoObj内にあるため、この値を参照するにはinfoObjが呼び出し元のオブジェクトである必要があります。しかし、コピーをするとcopyObjが呼び出し元のオブジェクトになるため、thisはグローバルオブジェクトを参照します。
本当にグローバルオブジェクトを参照しているか確認してみましょう。グローバルオブジェクトにプロパティを追加するには、window.プロパティ名 = 値の形で記述します。
window.name = "Michael"; // -> グローバルオブジェクトにnameプロパティを追加
const infoObj = {
name: "John",
sayName: function () {
console.log(`私の名前は${this.name}です`);
},
};
infoObj.sayName(); // -> 私の名前はJohnです
const copyObj = infoObj.sayName;
copyObj(); // -> 私の名前はMichaelです
実行してみると、コンソールにグローバルオブジェクトのnameプロパティに追加したMichaelが出力されていることが確認できます。
コールバック関数でthisを使った時の挙動
最後にコールバック関数でthisを使った時の挙動を確認してみましょう。先ほどのサンプルコードに少し改良を加えた以下のコードを使います。
window.name = "Michael";
const infoObj = {
name: "John",
sayName: function () {
console.log(`私の名前は${this.name}です`); // -> 私の名前はMichaelです
},
};
function infoFn(callback) {
callback(); // -> 渡されたinfoObj.sayName関数を実行
}
infoFn(infoObj.sayName); // -> infoObj.sayNameを実引数に設定
上記のコードを実行すると「私の名前はMichaelです」とコンソールに出力されます。これはコピーした時と同様にthisがグローバルオブジェクトを参照しています。
この理由もコピーした時と同様に、thisの呼び出し元のオブジェクトがinfoObjではなくなっていることが原因です。