クラス内でのアロー関数の使い方を、動くコード例・利点と注意点・実務での使い分けを交えてわかりやすくまとめます。
アロー関数は this を「定義場所(レキシカル)」から拾う 特性があるので、クラスのインスタンスメソッドを外部に渡す(コールバックに渡す、イベント登録する、タイマーで呼ぶ等)ときに this の binding 問題を回避できます。
使い方パターン(例を見ながら)
1) 普通のメソッド(プロトタイプ上)
class Timer {
constructor() {
this.count = 0;
}
tick() { // prototype に置かれるメソッド
this.count++;
console.log('tick', this.count);
}
}
const t = new Timer();
setInterval(t.tick, 1000); // ❌ this が失われる(グローバルや undefined になる)
JavaScript問題:setInterval に直接渡すと this が Timer のインスタンスではなくなる(bind されないため)。
2) コンストラクタで bind する(従来の回避策)
class Timer {
constructor() {
this.count = 0;
this.tick = this.tick.bind(this); // 明示的に this を固定
}
tick() {
this.count++;
console.log('tick', this.count);
}
}
JavaScript利点:プロトタイプの利点(メソッドは一つだけ)を活かしつつ this 問題を解決できる。欠点:コンストラクタでの明示バインドが煩雑になることがある。
3) クラスフィールドにアロー関数を使う(簡潔・自動バインド)
class Timer {
count = 0;
tick = () => { // インスタンスごとの関数(自動で this を正しく参照)
this.count++;
console.log('tick', this.count);
}
}
const t = new Timer();
setInterval(t.tick, 1000); // ✅ 正しく動く
JavaScriptポイント:
tickは各インスタンスごとに作られる(プロトタイプ上ではなくインスタンス自身のプロパティになる)。thisは常にそのインスタンスを参照するため、bind不要。- シンタックスは
class内でfield = () => {}の形(class fields)を使います。
インスタンスメソッド(arrow)とプロトタイプメソッドの違いまとめ
- プロトタイプメソッド(通常のメソッド)
- メモリ効率:全インスタンスで1つだけ(プロトタイプに存在)。
- 動作:
thisは呼び出し方に依存(外で渡すとthisが変わる)。 - 適している場面:
thisを直接使わないユーティリティ的なメソッドや、頻繁にインスタンスを作る場合。
- インスタンスプロパティとしてのアロー関数
- メモリ:インスタンス毎に関数オブジェクトが生成される(インスタンス×1)。
- 動作:作成時に
thisが固定(レキシカル)。外部に渡してもthisが壊れない。 - 適している場面:イベントハンドラ、コールバック、UIコンポーネントのメソッド(自動バインドが便利)。
実践例:イベントハンドラでの使い分け(ブラウザ想定)
<button id="btn">Click</button>
<script>
class Clicker {
constructor() {
this.count = 0;
// 方式A: class field arrow(おすすめ)
this.onClick = () => {
this.count++;
console.log('clicked', this.count);
};
// 方式B: prototype メソッド + bind(冗長)
// this.onClick = this.onClick.bind(this);
}
// prototype メソッド(もしこちらを使うなら bind が必要)
// onClick() {
// this.count++;
// console.log('clicked', this.count);
// }
}
const c = new Clicker();
document.getElementById('btn').addEventListener('click', c.onClick);
</script>
JavaScriptここでアロー関数(class field)を使っておくと、addEventListener に渡しても this が常に c を指すので安全です。
注意点・デメリット(知っておくべきこと)
- メモリ:インスタンスごとに関数が作られる → たくさんインスタンスを生成するアプリではメモリ負荷が増える場合がある。
- プロトタイプの利点がなくなる:継承やメソッド差し替えの振る舞いが従来と少し違う(メソッドはインスタンスプロパティのためプロトタイプ経由で差し替えづらい)。
- デバッグ表示:デバッガやプロファイラでメソッドがプロトタイプで見えないことがある(ただし大抵問題にならない)。
- 古い環境:
class fieldsやpublic class fieldsは古いブラウザではサポートされない場合があるので、サポートが必要ならトランスパイル(Babel等)を使う。 - 継承先でオーバーライドしたい場合:インスタンス関数だと継承で扱いが異なる(プロトタイプメソッドの方が継承向き)。
いつ使うか(実務的ガイド)
- UI コンポーネント(ボタンのハンドラやコールバック) → アロー関数(class field)推奨(簡潔でバグが減る)。
- ライブラリの基礎クラスや大量にインスタンス化する軽量オブジェクト → プロトタイプメソッド推奨(メモリ節約)。
- 継承階層が深く、メソッドのオーバーライドを多用する設計 → プロトタイプメソッド が適していることが多い。
演習(2問) — 手で書いて動かしてみよう
Workerクラスを作り、startメソッドをsetIntervalで1秒ごとにthis.countを増やしてログするようにしてください。アロー関数の class-field 版で実装して動作確認する。- 同じ
Workerをプロトタイプメソッドで実装してsetIntervalに渡した場合にthisがどうなるか試し、bindを使って直す方法も試す。
模範解答
演習①:アロー関数(class field)を使う版
<!DOCTYPE html>
<html>
<body>
<script>
class Worker {
count = 0;
// ✅ アロー関数で定義(自動で this が固定される)
start = () => {
console.log("Worker start!");
setInterval(() => {
this.count++;
console.log(`count: ${this.count}`);
}, 1000);
};
}
const w = new Worker();
w.start(); // ✅ 毎秒 count が 1, 2, 3... と増える
</script>
</body>
</html>
HTML💡 解説
startを「アロー関数」で定義しているため、thisは常にWorkerのインスタンスを指します。setIntervalのコールバックもアロー関数にしてあるので、中の this も固定されます。- つまり「どこから呼ばれても this が正しく保たれる」状態。
→bind()を書く必要がなく、非常にシンプル。
演習②:プロトタイプメソッドで this が消える例(→ bindで修正)
<!DOCTYPE html>
<html>
<body>
<script>
class Worker {
constructor() {
this.count = 0;
}
// ❌ 通常のメソッド(prototype上)
start() {
console.log("Worker start!");
setInterval(this.tick, 1000); // ← this.tick 内の this が失われる!
}
tick() {
this.count++;
console.log(`count: ${this.count}`);
}
}
const w = new Worker();
w.start(); // ⚠️ TypeError: Cannot read properties of undefined (reading 'count')
</script>
</body>
</html>
HTML❌ なぜエラー?
setInterval(this.tick, 1000)の時点で関数tickを「ただの関数」として渡している。- 関数単体で呼ばれると、
thisはundefined(またはグローバル)になるためthis.countが読めない。
修正版(bindを使って this を固定)
<!DOCTYPE html>
<html>
<body>
<script>
class Worker {
constructor() {
this.count = 0;
this.tick = this.tick.bind(this); // ✅ ここで this を固定!
}
start() {
console.log("Worker start!");
setInterval(this.tick, 1000); // ✅ これでOK
}
tick() {
this.count++;
console.log(`count: ${this.count}`);
}
}
const w = new Worker();
w.start(); // ✅ count: 1, 2, 3... と増える
</script>
</body>
</html>
HTML💡 解説
bind()を使うことでthisを強制的にインスタンスに固定。- この方法も正しく動くが、毎回コンストラクタで
bindが必要で少し冗長。 - クラスフィールド+アロー関数なら
bind不要でシンプルに書けます。
まとめ比較
| 方法 | 書き方 | this の扱い | メリット | デメリット |
|---|---|---|---|---|
| プロトタイプメソッド | start() {} | 呼び出し方で変化 | メモリ効率良い | 外部渡し時に this が失われやすい |
| bindで修正 | this.method = this.method.bind(this) | 固定 | 確実に動く | コンストラクタで毎回bindが必要 |
| アロー関数(class field) | method = () => {} | 自動固定 | シンプル・安全 | インスタンスごとに関数が増える(メモリ) |


