🦁

JavaScriptでメソッドにアロー関数はNG

に公開

昨日変な記事を見かけてコメントしたけど伝わらなかったようなので、記事にしときます。

お好きなAIに以下の質問をしてみましょう。

「JavaScriptでメソッドにアロー関数はNGですか?」

誰でも無料で使用できる匿名geminiさんはまずこう言ってます。

JavaScriptにおいて、オブジェクトのメソッドを定義する際にアロー関数を使うのは、一般的に推奨されませんし、多くの場合で意図しない挙動を引き起こすためNGと考えられています。

ChatGPTさん(ゲスト)だと…

結論から言うと:

✅ 「場合による」ですが、クラスやオブジェクトのメソッドとしてアロー関数を使うのは多くの場合NG(非推奨)です。

ここでは多くを語りません。自分で納得いくまで聞いてください。

以下では主にクラスプロパティの中に入れたケースの挙動を実装で追っていきます。

1. 普通のクラス

まずは何の変哲もない挨拶クラスです。

https://github.com/marudedameo2019/method_vs_arrow/blob/main/greeter.ts

実行します。

$ npx tsx greeter.ts
こんにちは

2. アロー関数をメンバに持つクラス

次はアロー関数をメンバ(プロパティ)に持つ挨拶クラスです。

https://github.com/marudedameo2019/method_vs_arrow/blob/main/greeter_arrow.ts

実行します。

$ npx tsx greeter_arrow.ts
こんにちは

一見違いがないように見えます。

3. 違い

違いを見ていきます。

3-1. プロパティの違い

最大の違いはコレです。

https://github.com/marudedameo2019/method_vs_arrow/blob/main/greeter_compare.ts

実行してみます。

$ npx tsx greeter_compare.ts
GreeterNormal { message: 'こんにちは' }
GreeterArrow { message: 'こんにちは', greet: [Function: greet] }

GreeterArrowだけgreetがプロパティになっていて、インスタンスごとに生成されていることが分かります。

3-2. コンストラクタの速度の違い

両者のコンストラクタの速度を実測します。

以下のコードはOBJ_COUNT(下のコード上は10000個です)で指定された数分両方のクラスのインスタンスを生成し、その時間を測定したものです。

https://github.com/marudedameo2019/method_vs_arrow/blob/main/greeter_compare_construct_speed.ts

実行してみます。

$ npx tsx greeter_compare_construct_speed.ts
constructor benchmark
┌─────────┬───────────┬───────────────────┬───────────────────┬────────────────────────┬────────────────────────┬─────────┐
│ (index) │ Task name │ Latency avg (ns)  │ Latency med (ns)  │ Throughput avg (ops/s) │ Throughput med (ops/s) │ Samples │
├─────────┼───────────┼───────────────────┼───────────────────┼────────────────────────┼────────────────────────┼─────────┤
│ 0       │ 'Normal'  │ '120071 ± 1.49%'  │ '112800 ± 1700.0' │ '8542 ± 0.81%'         │ '8865 ± 136'           │ 833     │
│ 1       │ 'Arrow'   │ '2217380 ± 3.81%' │ '2040000 ± 95550' │ '460 ± 3.20%'          │ '490 ± 24'             │ 64      │
└─────────┴───────────┴───────────────────┴───────────────────┴────────────────────────┴────────────────────────┴─────────┘

コンストラクタだけなので僅かな違いではありますが、アロー関数を持つクラスのコンストラクタは約18倍遅くなっています。同じことをわざわざ遅く実装する必要がありません。

3-3. 消費メモリの違い

node.jsでの消費メモリの違いを見てみます。

https://github.com/marudedameo2019/method_vs_arrow/blob/main/greeter_compare_memory.ts

※最後に双方最終要素だけ表示してるのはgcされないための配慮です

実行してみます。

$ npx tsx greeter_compare_memory.ts
GreeterNormal(rss): 656 KB
GreeterNormal(heapUsed): 507.6953125 KB
GreeterArrow(rss): 4704 KB
GreeterNormal(heapUsed): 3187.3359375 KB
GreeterNormal { message: 'こんにちは' }
GreeterArrow { message: 'こんにちは', greet: [Function: greet] }

大した量ではありませんが、アロー関数を持つクラスは約7倍のメモリ消費をしています。

4. まとめ

クラスのメソッド的にアロー関数を使用すると、調査環境では18倍程度遅くなり、メモリ消費が7倍程度になった

5. 最後に

クラスのメソッドにアロー関数の使用はお勧めしません。

コールバックなどのときにthisを明示的にbindしなくても良いなどの話もなくはないのですが、個人的な意見としては、そういうケースでそこを気にするくらいなら、thisを使用するよりもクロージャーの中で定義した変数を参照した方が安全だと思います。オブジェクト自体のプロパティに混入してしまうのは悪手かと…

GitHubで編集を提案

Discussion