関数をもっと短く書く方法
JavaScriptのアロー関数は、関数をより簡潔に書くための構文です。ES2015で導入されて以来、小さなインライン関数の書き方としてすっかり定番になりました。特に、配列メソッドに渡すコールバックや、Promiseのハンドラー、イベントリスナーなどでよく使われます。
書き方はシンプルで、引数、=>、本体の順に並べるだけです。
クラシックな書き方と同じ結果になります。
const add = function (a, b) {
return a + b;
};
タイピングが減るのもうれしいポイントですが、アロー関数を使う本当の理由は暗黙のreturnと**thisの扱い方**にあります。どちらもこのあと順番に見ていきましょう。
アロー関数の暗黙のreturn
関数の中身が1つの式だけなら、波カッコと return を省略できます。書いた式の値が自動的に返される仕組みです:
アロー関数の魅力がいちばん発揮されるのがここ。1行で書ける変換処理は、まるで数式のようにスッと読めます。
複数の処理をまとめて書きたくなった瞬間、中かっこと明示的な return の出番に戻ります。
波括弧の中で return を書き忘れるのは、アロー関数で一番よくあるミスです。x => { x * 2 } は undefined を返してしまいます。波括弧で囲んだ瞬間に中身はブロック扱いになり、式の結果はそのまま捨てられてしまうからです。
引数のカッコ
引数がちょうど1つのときは、カッコを省略できます:
引数が 0 個、もしくは 2 個以上あるときは、括弧が必須になります。
チームによっては、統一感を重視して常に括弧を付けるスタイルを好むこともあります。どちらでも構わないので、どちらかに決めたら一貫して使いましょう。
オブジェクトリテラルを返すとき
ここでよくハマるポイントがあります。オブジェクトを暗黙のreturnで返そうとすると、波括弧が関数本体と解釈されてしまうのです。
これだと undefined が出力されてしまいます。JavaScriptは { name: name } をオブジェクトリテラルではなく、ラベル付き文を含むブロックとして解釈してしまうからです。オブジェクトを丸カッコで囲んで式として扱わせましょう。
{ ... } を () で囲むのが正解です。これはしっかり覚えておいてください。アロー関数を使い始めて1週間以内に必ずぶつかるポイントです。
レキシカル this とは
アロー関数が存在する本当の理由は、記法が短くなるからではありません。自分自身の this を持たず、外側のスコープから this を受け継ぐという点にあります。
なぜこれが重要なのか。通常の関数をコールバックとして使った例を見てみましょう。
setInterval に渡した function () { ... } の中では、this は counter を指しません。通常の関数は呼び出され方によって独自の this を持つので、setInterval からコールバックが呼ばれるときの this は undefined(strict モードの場合)か、もしくはグローバルオブジェクトになってしまうんです。
一方、アロー関数なら外側のメソッドの this をそのまま引き継いでくれます。
アロー関数の中でも this は counter のままです。アロー関数は自分自身の this を持たないからですね。アロー関数が登場する前は、const self = this; や .bind(this) でこの問題を回避するのが定番でした。今でも使える書き方ではありますが、ほぼ出番はなくなりました。
アロー関数が「持たないもの」
レキシカルthisの挙動は、実はもっと大きな特徴の一部です。アロー関数は、通常の function が持っているいくつかの機能をあえて持っていません。
- 自分の
thisを持たない — 外側のスコープから引き継ぎます。 argumentsオブジェクトを持たない — 代わりにレストパラメータ(...args)を使います。newで呼び出せない — コンストラクタとしては使えません。prototypeプロパティを持たない —newで呼べない以上、当然と言えます。
...args を使えば「任意の個数の引数を受け取る」という arguments と同じことができて、しかも受け取ったものがちゃんとした配列になるというオマケ付きです。new Greeter(...) の呼び出しがエラーになるのは、アロー関数がコンストラクタとして使えないためです。
アロー関数を使わないほうがいい場面
コールバックを書くときはアロー関数が第一候補になりますが、次のようなケースでは向いていません。
アロー関数でオブジェクトのメソッドを定義すると、this はそのオブジェクトにバインドされません。オブジェクトリテラルが書かれた場所(たいていはモジュールやグローバルスコープ)の this をそのまま引き継いでしまいます。メソッドを定義するときは、メソッドの短縮構文(greet() { ... })を使いましょう。
クラスのプロトタイプメソッド、this が要素を指してほしいイベントハンドラ、それから new で呼び出すつもりの関数についても同じ話です。こうした場面では、普通の function が正解です。
ざっくりした使い分けの目安
- 短いコールバックで、式がひとつだけ? → アロー関数。
thisを外側のスコープのまま保ちたい? → アロー関数。- オブジェクトやクラスのプロトタイプにメソッドを定義する? → 通常の関数(もしくはメソッド短縮構文)。
- コンストラクタを書く? → 通常の関数、あるいはそれよりも
classを使うのがおすすめ。
現場の JavaScript コードを読めば、どちらのスタイルも当たり前に出てきます。どっちがフィットするかの感覚は、他人のコードを数週間読んでいるうちに自然と身につき、そのあとは意識しなくても選べるようになります。
次は: 引数とデフォルト値
引数まわりの機能——デフォルト値、レストパラメータ、分割代入された引数など——は、アロー関数でも通常の関数でも同じように使えます。次のページで扱うテーマですが、ここまでに見てきたすべての関数の書き方に共通する内容です。
よくある質問
アロー関数とは何ですか?
アロー関数は=>を使って関数を短く書くための構文です。たとえばconst add = (a, b) => a + b;は、2つの引数を受け取って合計を返すアロー関数です。見た目が短いだけでなく、this・arguments・superを自前で持たず、外側のスコープのものをそのまま引き継ぐのが大きな特徴です。
アロー関数と通常のfunctionは何が違いますか?
functionで定義した関数は独自のthisとargumentsを持ち、newでコンストラクタとしても使えます。一方、アロー関数はそのどれも持たず、thisは定義された場所のスコープから引き継がれます。さらに、関数宣言のような巻き上げ(ホイスティング)も発生しません。
アロー関数はどんなときに使えばいいですか?
arr.map(x => x * 2)のような短いコールバックや、クラスのメソッド内でsetTimeoutやイベントリスナーにハンドラを渡すときなど、thisを外側のスコープに固定したい場面で活躍します。逆に、オブジェクトのメソッド、コンストラクタ、トップレベルで定義する独立した関数は、通常のfunctionで書いたほうが素直で読みやすくなります。