「this、思った通りに動いてくれない…」——こんなモヤモヤ、開発現場で何度も経験しませんか?
通常関数やアロー関数、クラスやbind。どれも“this”の参照先が微妙に違い、バグや手戻り、レビュー指摘の“元凶”になりがちです。
なぜこんなに複雑? どう選べば安全?
私たちが今すぐ腹落ちできるよう、「仕組み → ミス例 → 解決策」を短く分解して解説します。
- thisの基本仕様・違いを体系的に整理
- バグ・手戻りを防ぐための現場ノウハウ
- チームレビューや教育にも使えるチェックリスト
用語解説:this
JavaScriptで関数やメソッドが参照する「実行中のオブジェクト」。呼び出し方によって指すものが変わる。用語解説:アロー関数
ES6で導入された短い記法の関数。定義時の外側スコープのthisを参照し、通常の関数とは挙動が異なる。用語解説:bind
関数のthisを特定の値に固定するためのメソッド。新しい関数を返す。用語解説:クラス
ES6以降で使えるオブジェクト設計の仕組み。インスタンス生成や継承が可能。
1. なぜJavaScriptのthisは迷いやすい?現場あるあるの混乱
「thisがundefined?」
「イベント内でthisが期待通りじゃない…」
実際、現場でこんな失敗がよく起きています。
- 関数やメソッドの呼び出し方でthisがグローバルオブジェクトやundefinedになる
- コールバック・イベントリスナーでthisが“変化”する
- アロー関数でthisが“外側”を参照してバグ発生
- クラス継承やプロトタイプチェーンで挙動が読みにくくなる
こうした混乱の背景には、thisが「呼び出し方」で決まるというJavaScript独特の仕様があります。
(「undefined」やthisの混乱については『なぜ「undefined」が出る?原因と解決法10選|JavaScript のデバッグに効く実例集』もご参照ください)
2. JavaScriptのthisとは?仕組みと決まり方
thisは、関数やメソッドの「呼び出し方」によって自動的に値が決まります。
- グローバル呼び出し:通常関数を直接呼ぶとthisはグローバルオブジェクト(ブラウザならwindow、厳格モードはundefined)
- オブジェクトのメソッド呼び出し:オブジェクトのメソッドなら、そのオブジェクト自身
- コンストラクタ(new):新しいインスタンス自身
- 明示的なbind/call/apply:指定した値
- アロー関数:定義時の“外側スコープ”のthisをそのまま使う(変化しない)
「thisはどこを指すのか?」は呼び出し方ごとに整理して覚えましょう。
(letやvarの違いによるthisの混乱については『図解でわかる! JavaScript初心者がハマる letとvarの違いを徹底解説|現場での注意点まで完全対応』もご参照ください)
3. 構文別thisの違いと使い分け【表&コード例】
■ 通常関数 vs アロー関数の“this”違い
通常関数は「呼び出し元」、アロー関数は「定義時の外側」を参照します。
日常生活の例え:
「通常関数」は“誰が呼ぶか”で自分(this)が変わるイメージ。
例えば「家で『ご飯食べる』と言えば自分の家で食べる」、「友達の家で『ご飯食べる』と言えば友達の家で食べる」ように、呼び出し元によって“自分”が変わります。
一方「アロー関数」は“どこで決めたか”が基準。
例えば「お母さんが『ご飯食べる』と言ったら、いつ誰が呼んでも“お母さん”がご飯を食べる」ように、定義した時点の“自分”がずっと使われます。
const obj = {
value: 42,
normalFunc: function() { return this.value; },
arrowFunc: () => this.value
};
// obj.normalFunc() → 42
// obj.arrowFunc() → undefined
コールバックやイベントリスナーでは、アロー関数だとthisが想定外になることが多いので注意!
■ bind/call/applyでthisをコントロール
- bind:thisを“固定”した新しい関数を返す
- call/apply:その場でthisを指定して関数実行(引数の渡し方が違う)
イベントリスナーで「thisがバグる」場合、bindで明示的に固定するのが定番の対策です。
■ クラス・継承・TypeScript環境でのthis
クラス(ES6以降)では、通常メソッドのthisはインスタンス自身ですが、コールバックとして渡すとthisが失われることが。
TypeScriptではthisの型安全も重要なので、設計段階からthisの流れを意識しましょう。
(TypeScriptやクラス設計の基礎については『TypeScriptとは?JavaScriptとの違いを初心者向けにわかりやすく解説』もご参照ください)
4. コールバック・イベント・非同期でのthisバグと対策
現場で本当に多いのが、コールバックやsetTimeout、イベントリスナーでthisが変化するパターン。
あるあるバグ例
- setTimeout内でthisがグローバルやundefinedになる
- イベントリスナーでthisが要素以外を参照
解決法
- アロー関数で外側のthisを「捕捉」
- bindでthisを明示的に固定
- クラスならconstructor内でbindを仕込む
「thisをどこで束縛するか?」をチームで明文化しておくと安心です。
(コールバックや非同期処理の詳細は『JavaScriptのコールバックがわからない?アニメーション実例でやさしく解説!』もご参照ください)
5. 受託・SES現場で本当にあったthisバグとリファクタ
現場で頻発するのは、クラスのメソッドをイベントリスナーなどにそのまま渡してthisが失われるパターン。
リファクタ例
- 誤:button.addEventListener(‘click’, this.handleClick);
- 正:button.addEventListener(‘click’, this.handleClick.bind(this));
またはthis.handleClick = this.handleClick.bind(this);をconstructorで実行
「イベントに渡すときは必ずthisを固定」
こうしたルールをプロジェクトで共有し、再発防止につなげましょう。
6. チーム開発で役立つthisチェックリスト&レビュー観点
バグや手戻りを減らすため、コードレビューで次を必ずチェック!
- コールバック・イベントでthisが想定通りか
- アロー関数/通常関数の選択が適切か
- クラスメソッドのthisが“失われて”いないか
- TypeScriptでthisの型が安全か
レビュー時、「この関数のthisは何を指している?」とチーム内で確認する文化をつくるのもおすすめです。
7. よくある疑問Q&A
- Q1. JavaScriptのthisはなぜ状況ごとに変わるの?
A. 呼び出し方ごとにthisが自動で変わる仕様だからです。 - Q2. アロー関数でthisが想定外になるのはなぜ?
A. アロー関数は定義時の外側スコープのthisを参照し、オブジェクトメソッドには向きません。 - Q3. bind/call/applyの違いは?
A. bindは新しい関数、call/applyは即時実行。どれもthisを指定できます。 - Q4. クラス継承でのthis注意点は?
A. メソッドをコールバックで渡すとthisが失われやすいのでbindやアロー関数で工夫を。 - Q5. コールバックやsetTimeoutでthisが変わる理由は?
A. 呼び出し元が異なるため、thisが思わぬ値に変化します。 - Q6. イベントリスナーでthisを意図通り扱うには?
A. bindやアロー関数で固定しましょう。 - Q7. チームでthis誤用を減らすコツは?
A. チェックリストやペアプロ、レビュー時の確認が有効です。
まとめ:this迷子から抜け出すために
thisは一見シンプルですが、構文や場面ごとに参照先が激変します。
今回ご紹介した比較表やコード例、チェックリストを現場に持ち帰り、日々の開発で「this迷子」を減らしていきましょう。
ぜひコードをコピペして、まずは動かしてみてください。