JavaScriptのthisの動作を関数で理解しよう!
生徒
「先生、JavaScriptの『this』ってよく聞きますけど、何のことかよくわかりません。教えてください!」
先生
「『this』はJavaScriptでとても大事なキーワードで、関数の中で『今使っているそのもの』を指すものなんです。」
生徒
「『今使っているそのもの』って、具体的にはどういうことですか?」
先生
「たとえば、自分がどの部屋にいるかを表す住所のようなものだと思ってください。場所によって『this』が指すものが変わるんですよ。」
生徒
「じゃあ、『this』はいつも同じものを指すわけじゃないんですね?」
先生
「その通り!『this』は関数がどのように呼ばれたかによって変わるんです。これを理解するとJavaScriptがもっと楽しくなりますよ!」
1. thisとは何か?
JavaScriptのthisは「この関数やメソッドが動いているときの対象」を指す特別なキーワードです。
英語の「this」は「これ」や「このもの」という意味で、プログラム内でも「今扱っているもの」を表します。
つまり、thisは関数やオブジェクトの文脈(呼び出し方)によって変わる、動的な「自分自身」のことです。
2. 関数内のthisの基本動作
まず、関数内でのthisの基本は「関数が呼ばれた方法によって変わる」ことです。
たとえば、グローバルな関数の場合、ブラウザではthisは「windowオブジェクト」を指します。
function showThis() {
console.log(this);
}
showThis(); // window(ブラウザの場合)
このように、普通に関数を呼ぶとthisはグローバルなオブジェクトを指します。
3. オブジェクトのメソッド内でのthis
オブジェクトの中で関数(これをメソッドと呼びます)を使うと、thisはそのオブジェクトを指します。
const person = {
name: "太郎",
greet: function() {
console.log(this.name + "さん、こんにちは!");
}
};
person.greet(); // 太郎さん、こんにちは!
ここではthisがpersonオブジェクトを指しているので、this.nameはperson.nameと同じです。
4. thisが関数呼び出しで変わる理由
JavaScriptではthisは「関数がどのオブジェクトに属して呼ばれたか」で決まります。
だから、同じ関数でも呼び出し方によってthisの中身が変わることがあります。
const person = {
name: "花子",
greet: function() {
console.log(this.name + "さん、こんにちは!");
}
};
const greetFunc = person.greet;
greetFunc(); // undefinedさん、こんにちは!(またはwindow.nameの値)
この例では、greetFuncはただの関数なので、thisはグローバルオブジェクト(ブラウザではwindow)を指し、nameがないためundefinedになります。
5. thisを明示的に指定する方法
thisを自分で指定したいときは、callやapply、bindというメソッドを使います。
たとえば、callを使うと次のようになります。
function greet() {
console.log(this.name + "さん、こんにちは!");
}
const person = { name: "次郎" };
greet.call(person); // 次郎さん、こんにちは!
この例では、greet関数をpersonオブジェクトのthisとして呼んでいます。
6. アロー関数とthisの違い
JavaScriptのアロー関数は普通の関数と違い、thisを自分で持ちません。
アロー関数内のthisは、外側の関数やオブジェクトのthisをそのまま使います。
const person = {
name: "三郎",
greet: () => {
console.log(this.name); // undefined(グローバルのthis)
}
};
person.greet();
このようにアロー関数ではthisが期待したオブジェクトを指さない場合があるので、注意が必要です。
7. まとめないけど最後にポイントだけ
thisは「関数が呼ばれた状況によって変わる特別なキーワード」- オブジェクトのメソッドでは
thisはそのオブジェクトを指す - ただの関数呼び出しではグローバルオブジェクトを指す(ブラウザならwindow)
callやapply、bindでthisを指定できる- アロー関数は自分の
thisを持たず、外側のthisを使う
JavaScriptのthisは最初は難しく感じますが、たくさんコードを書いて慣れると自然に理解できるようになります!
まとめ
ここまでJavaScriptにおける「this」の振る舞いについて詳しく解説してきました。JavaScriptを学び始めたばかりの方にとって、この「this」という概念は非常に厄介で、時に挫折の原因にもなりかねない難解なテーマです。しかし、この記事を通して見てきたように、その正体は「実行時に決まるコンテキスト(文脈)」に他なりません。
thisを攻略するための重要な視点
プログラムを書く際、私たちはついつい「ソースコード上の場所」で変数の意味を固定して考えてしまいがちです。しかし、JavaScriptのthisは「どこに書かれているか」よりも「どのように呼び出されたか」が優先されるという動的な性質を持っています。これを「ダイナミックバインディング(動的結合)」と呼びます。
例えば、同じ関数であっても、単独で実行される場合と、オブジェクトのプロパティとして実行される場合では、その中身(参照先)が全く異なります。この柔軟性こそがJavaScriptの強力な武器であると同時に、バグを生み出しやすい落とし穴でもあるのです。
さらに理解を深めるための実践コード
実際に、もう少し複雑なケースでthisがどう変化するかを確認してみましょう。クラス構文(Class)やコンストラクタ関数においても、thisは頻繁に使用されます。
class UserProfile {
constructor(userName) {
this.userName = userName;
}
display() {
console.log("ユーザー名: " + this.userName);
}
}
const user1 = new UserProfile("テック太郎");
user1.display();
実行結果は以下のようになります。
ユーザー名: テック太郎
この場合、new演算子を使ってインスタンスを生成することで、thisは新しく作られたオブジェクト自身を指すようになります。これも「呼び出し方」による変化の一種ですね。
SEOを意識したモダンなJavaScript開発
現代のWebフロントエンド開発、特にReactやVue.jsといったフレームワークを使用する環境においても、thisの理解は不可欠です。最近では関数型プログラミングのスタイルが主流になり、thisを意識しなくても済むアロー関数やHooksの利用が増えていますが、既存のライブラリを拡張したり、デバッグを行ったりする際には、基盤となるthisの知識がなければ太刀打ちできません。
「なぜ、ここでメソッドがundefinedになってしまうのか?」「なぜ、イベントリスナーの中のthisがボタン要素を指してしまうのか?」こうした疑問に直面したとき、この記事で学んだ基本原則に立ち返ってみてください。
- 暗黙的なバインド: オブジェクトのメソッドとして呼ばれた場合。
- 明示的なバインド: call, apply, bind を使って強制的に指定した場合。
- newバインド: コンストラクタとしてインスタンス化した際。
- デフォルトバインド: 厳格モード(strict mode)でない場合のグローバルオブジェクト。
これら4つのパターンを整理して覚えておくだけで、コードの読みやすさとデバッグのスピードは劇的に向上します。JavaScriptのスキルをワンランク上の「中級者」へと引き上げるために、thisの動きを完全にマスターしましょう。
生徒
「先生、まとめを読んでさらによくわかりました!結局、thisは『誰がその関数を呼び出したのか』という犯人探しみたいなものなんですね。」
先生
「ははは、面白い表現ですね!まさにその通りです。『誰の持ち物として実行されたか』を常に意識するのがコツですよ。」
生徒
「でも、アロー関数のときだけルールが違うのがちょっとややこしいです。アロー関数は自分のthisを持たないんですよね?」
先生
「ええ、アロー関数は『書かれた場所』で固定されるんです。これをレキシカルスコープと言います。逆に言えば、周りの環境のthisをそのまま使いたいときにはアロー関数が最強の味方になりますよ。」
生徒
「なるほど!例えばタイマー処理の setTimeout の中でオブジェクトのデータを使いたいときとかに便利そうですね。」
先生
「その通り、素晴らしい気づきです!具体的にどう書くか、サンプルを見てみましょうか。」
const timer = {
seconds: 0,
start: function() {
setInterval(() => {
this.seconds++;
console.log(this.seconds + "秒経過...");
}, 1000);
}
};
// timer.start(); // これで正しく動く!
生徒
「わあ、本当だ!普通の関数だと this.seconds がエラーになっちゃうけど、アロー関数なら timer オブジェクトの seconds をちゃんと見てくれるんですね。」
先生
「そうなんです。JavaScriptの進化とともに、thisの扱いも便利になってきました。でも、昔ながらのコードを読むときには、callやapplyの知識も絶対に必要になります。まずは基本をしっかり押さえておけば、どんな複雑なプログラムに出会っても怖くありませんよ。」
生徒
「ありがとうございます!thisがわかると、JavaScriptのドキュメントを読むのがずっと楽になりそうです。もっと色んなコードを書いて試してみます!」
先生
「その意気です!もし迷ったら、いつでも console.log(this) を書いて、正体を確認する癖をつけてみてくださいね。」