早期 return でネストを浅くする
このレッスンで分かること
returnは関数をその場で終わらせるので、不正な入力を冒頭で追い返せます- ガード節を並べると
elseが消えて、本体が一番浅い位置に来ます
条件が 1 つ増えるたびに、本体が奥へ埋まる
第 4 章で if と else を学んだとき、条件が増えるほどコードが右へずれていくのを体験したはずです。あのときは我慢するしかありませんでした。return をまだ知らなかったからです。
作品カードの 1 行を作る関数で、入力を 3 つ検査してみます。
JavaScript
function workCard(title, views) {
if (title) {
if (typeof views === "number") {
if (views >= 0) {
return title + " 閲覧 " + views + " 回";
} else {
return "表示できません";
}
} else {
return "表示できません";
}
} else {
return "表示できません";
}
}やりたいことは 1 行だけなのに、3 段の奥に埋まっています。else がどの if のものか目で追う必要があり、検査をもう 1 つ足すには本体を丸ごと包み直すことになります。
入口で追い返せば、本体は一番浅い位置に来る
return には「値を返す」だけでなく「そこで関数を終わらせる」働きがありました。これを使って、通したくない入力を冒頭で帰らせます。これをガード節、または早期 return と呼びます。
JavaScript
function workCard(title, views) {
if (!title) return "表示できません";
if (typeof views !== "number") return "表示できません";
if (views < 0) return "表示できません";
return title + " 閲覧 " + views + " 回";
}行数はほとんど変わりませんが、読みやすさは別物です。
| 観点 | ネスト版 | ガード節版 |
|---|---|---|
| 本体のインデント | 3 段の奥 | 一番浅い位置 |
else の数 | 3 個 | 0 個 |
| 検査を 1 つ足す手間 | 全体を包み直す | 1 行足すだけ |
門番を並べておいて、引っかかった呼び出しにはその場で帰ってもらう。門を全部通り抜けた呼び出しだけが本体に届くので、本体は正常な値だけを想定して書けます。最初の数行を読むだけで、その関数がどんな入力を受け付けるのかが分かります。コメントを書かなくても、コードそのものが入力の仕様書になります。
ガード節を書いたら、else は要らない
if の中で return している以上、その先へ進むのは条件が偽だったときだけです。else は何の情報も足していません。
JavaScript
// 冗長
if (views < 0) {
return "表示できません";
} else {
return "OK";
}
// すっきり
if (views < 0) return "表示できません";
return "OK";もう 1 つ大事なのが、はじいたときの戻り値をそろえることです。あるときは null、あるときは空文字列、あるときは文言、とばらばらだと、呼び出す側が毎回違う判定を書くはめになります。異常のときは必ずこれを返す、と 1 つに決めてください。
elseが悪いわけではありません。同じ重みの選択肢が並ぶときはif / else if / elseが正解です。ガード節は異常なケースを先に落とすための道具で、対立する考え方ではありません。
よくある間違い
returnを書かずに値だけ置く —if (!title) { "表示できません"; }では何も起きません。判定はしていますが関数は終わらず、そのまま下の行へ進みます!を付け忘れる — ガード節は否定形で書く場面が多く、if (title) return "表示できません";と書くと、名前があるときにはじくという逆の動きになります。書いたあとに「この条件が真のとき何が起きるか」を一度声に出して確かめてください
要件
- 関数名は greetUser、引数は name と age の 2 つにすること
- 不正な入力は関数の冒頭でガード節を使って return すること
- エラー時の戻り値は文字列
入力エラーに統一すること - 正常時は
こんにちは、NAME さん (AGE 歳)の形の文字列を返すこと
入出力例
greetUser("山田 太郎", 20) → "こんにちは、山田 太郎さん (20 歳)"
greetUser("山田 太郎", 0) → "こんにちは、山田 太郎さん (0 歳)"
greetUser("", 20) → "入力エラー"
greetUser("山田 太郎", -1) → "入力エラー"
greetUser("山田 太郎", "20") → "入力エラー"