Q1const twice = n => n * 2; を宣言して twice(4) を表示すると、何が出ますか?
アロー関数 — 省略形とオブジェクトを返す書き方
JavaScript のアロー関数を基礎から学びます。=> による書き換え、引数の丸括弧と本体の波括弧を省く省略形、オブジェクトを返すときの丸括弧、複数行のブロック本体を扱います。
配列の要素を 1 件ずつ整形するような短い処理でも、関数式で書くと function と波括弧と return が毎回並び、処理の中身が記号に埋もれます。
この記事では、=> で関数を作るアロー関数と、丸括弧や波括弧を省ける省略形を扱います。
同じ処理を短く書く — アロー関数の基本形
購入金額からポイント付与額を出すように、受け取った値を 1 つ計算して返すだけの関数はよくあります。それでも function の語と波括弧と return を毎回書くため、関数式が並ぶと計算の中身より書き方の部品のほうが目立ちます。
アロー関数(=> を使って関数を作る書き方)は const 名前 = (引数) => { 本体 }; と書きます。function の語を消し、引数の並びと本体の間に => を置いた形で、関数式と同じように変数へ代入して呼び出します。
// これまでの書き方: 関数を値として変数に代入する関数式
const pointBack = function (price) {
return price * 0.02;
};
// アロー関数
const pointBackArrow = (price) => {
return price * 0.02;
};
console.log(pointBack(3200)); // 64
console.log(pointBackArrow(3200)); // 64
console.log(typeof pointBackArrow); // function
比較のため pointBack と pointBackArrow に名前を分けていますが、どちらも typeof は function で、引数の受け取り方も return の働きも同じです。この基本形から、条件がそろうと括弧や return を省けます。
括弧と return を省く — 1 引数と暗黙の return
受け取った金額に送料を足す、5000 円以上かを判定するといった、式 1 つで済む処理があります。基本形のアロー関数だと 1 行の計算にも波括弧と return を含めて 3 行を使い、こうした関数が並ぶと行の大半を括弧と return が占めます。
引数がちょうど 1 つなら、引数を囲む丸括弧を省けます。さらに本体が式 1 つだけなら、波括弧と return も省けます。このとき働くのが暗黙の return(本体の式の値がそのまま戻り値になる決まり)です。
// 基本形: 引数の丸括弧・本体の波括弧・return をすべて書く
const withFee = (price) => {
return price + 500;
};
// 省略形
const withFeeShort = price => price + 500;
const isFreeShipping = total => total >= 5000;
console.log(withFee(1200)); // 1700
console.log(withFeeShort(1200)); // 1700
console.log(isFreeShipping(4800)); // false
isFreeShipping の本体は比較の式 1 つなので、4800 を渡すと 4800 >= 5000 の結果の false がそのまま戻ります。暗黙の return で戻るのは式の値なので、計算でも比較でも同じ形で書けます。
引数が 0 個や 2 個のときは丸括弧を省けません
() => 式 のように引数が無い場合と、(price, rate) => 式 のように 2 つ以上ある場合に丸括弧を外すと、SyntaxError になって実行そのものが始まりません。引数を後から増やすときは、丸括弧の付け直しが要ります。引数が 1 つでも (price) => 式 と丸括弧を残して書けます。
オブジェクトを返す — 丸括弧で包む書き方
1 件のデータを画面用の形に整えるとき、返したいのは 1 つの数値ではなく、複数のキーを持つオブジェクトです。ところが省略形のまま => の後ろに波括弧を書くと、オブジェクトではなく undefined が返ってきます。
=> の後ろに書いた波括弧はブロック本体(文をいくつでも並べられる領域)として読まれ、オブジェクトとしては読まれません。オブジェクトを返すときは (name) => ({ name: name }) のように丸括弧で包み、波括弧を式として読ませます。
// 波括弧をそのまま書くと、ブロック本体として読まれる
const toBadge = (name) => { label: name };
console.log(toBadge("ワイヤレスマウス")); // undefined
// 丸括弧で包むと、波括弧がオブジェクトとして読まれる
const toCard = (name, stock) => ({ name: name, inStock: stock > 0 });
const card = toCard("ワイヤレスマウス", 4);
console.log(JSON.stringify(card)); // {"name":"ワイヤレスマウス","inStock":true}
console.log(card.inStock); // true
キーを 2 つ以上書いた { name: name, inStock: true } は文として読めないため、SyntaxError で実行が始まりません。キーが 1 つのときは文として読めてしまい、エラーにならず undefined が返るので、表示するまで気づきにくくなります。
処理が複数行になるとき — ブロック本体と return
返品の受付期限のように、返す文言を決める前に途中の日数を計算したり、条件で分けたりする処理があります。式 1 つの省略形には、途中の値を置いておく場所も、条件ごとに返す値を変える場所もありません。
ブロック本体には const や if の文を並べられますが、暗黙の return は働かないため、値を返すには return を書きます。return は 1 つの関数に複数書け、先に実行された行で関数が終わります。
// 式 1 つの本体: 式の値がそのまま戻る
const shortMessage = (days) => `購入から ${days} 日`;
console.log(shortMessage(12)); // 購入から 12 日
// ブロック本体: 途中の値を作り、条件ごとに return する
const returnMessage = (days, isOpened) => {
const limit = isOpened ? 7 : 14;
if (days > limit) {
return "受付は終了"; // ここで関数が終わる
}
return `あと ${limit - days} 日`;
};
console.log(returnMessage(12, false)); // あと 2 日
console.log(returnMessage(12, true)); // 受付は終了
// return が無いと undefined
const brokenMessage = (days) => { `あと ${days} 日`; };
console.log(brokenMessage(12)); // undefined
条件に合わない場合を先に return で返しておくと、その後ろの行には通常の場合の処理だけを書けば済みます。下の表は、本体の書き方ごとに、呼び出し側へ返る値を並べたものです。
| 本体の書き方 | 本体の中で起きること | 呼び出し側に返る値 |
|---|---|---|
| days => 式 | 式の値がそのまま戻る | 購入から 12 日 |
| (days, isOpened) => { 文と return } | 実行された return の値が戻る | あと 2 日 / 受付は終了 |
| days => { 式だけ } | 式は評価されるが外へ渡らない | undefined |
this の扱いが通常の関数と違います
オブジェクトのメソッドの中では this(そのメソッドを呼び出したオブジェクト自身を指す語)が使えます。アロー関数はこの this を自分では持たず、外側で決まっているものをそのまま使います。この違いはクラスを扱うオブジェクト指向の章で改めて確認します。
理解度チェック
まずは1問ずつ答えてみましょう。
Q2const toItem = name => { label: name }; を呼び出したときの戻り値はどれですか?
Q3引数が 2 つのアロー関数で、引数を囲む丸括弧を省いて書くとどうなりますか?