TypeScriptでは、クラス内のメソッドに対するthisの型はデフォルトで現在のクラスインスタンスを指します。しかし、アロー関数でメソッドを定義すると、thisのコンテキストは呼び出し時ではなく定義時にバインドされます。
通常のメソッド:
class Counter { value = 0; increment() { this.value++; } }
アロー関数:
class Counter { value = 0; increment = () => { this.value++; } }
注意点:
class Foo { bar(this: Foo) { // ... } }
increment()とincrement = () => {}の違いは何ですか?コールバックとして使用した場合、thisのコンテキストにどのように影響しますか?
間違った回答:
正しい回答:
setTimeout(counter.increment, 0)のように、thisはundefined(strict mode)またはwindow(non-strict mode)になりますが、アロー関数はその周囲を保持します。class Demo { value = 1; inc() { console.log(this.value); } incArrow = () => { console.log(this.value); } } const d = new Demo(); setTimeout(d.inc, 0); // undefined またはエラー setTimeout(d.incArrow, 0); // 1
逸話
リアクティブフレームワークのプロジェクトでは、クラスメソッドが明示的なbindなしでコールバックとして直接渡されていました。その結果、thisはundefinedになり、thisプロパティにアクセスするときにアプリケーションがエラーでクラッシュしました。この問題は、メソッドをアロー関数に書き換えるか、明示的にbindすることで解決されました。
逸話
開発者はメソッド内でthisの型を明示したが、そのメソッド内のアロー関数の型付けを忘れました。その結果、ネストされたコールバック内のthisはクラスインスタンスではなくwindowを指していました。チームは状態漏れに直面し、イベントアーキテクチャを再構築する必要がありました。
逸話
大きなUIコンポーネントはアプリケーションを遅くしました。なぜなら、すべてのメソッドがクラスのアロー関数として記述されていたため(new Counter().increment = ...)、毎回新しい関数のコピーが各インスタンスのために作成され、通常のメソッドのようにプロトタイプでの1つの定義がなかったからです。その結果、メモリ使用量が増え、最適化が必要になりました。