Web制作やWebデザイナーの方で初めてJavaScriptを勉強してみると、条件分岐あたりでHTMLとCSSには無いプログラムっぽさを体験すると思います。
理屈はそんなに難しくないのですが、実はテクニックを覚えておくことで更に分かりやすく作ることができます。
「条件分岐が何かは分かるけど、自分でゼロから作るのは難しい」
「ネストはダメ!とか言われるけど、複雑な条件のときにネストするしかないと思っている」
本日はそんな方に向けたJavaScriptでの条件分岐についての便利テクニックを紹介します。
また動画もあるので必要に応じて使ってください。
JavaScriptの条件分岐でifの中でreturnがあるのはなぜか?
ググったり他人のコードを見ていると以下のような書き方を見かけたことはないでしょうか?
if (nubmer == null) return console.log("null");
コードの意味は一旦置いといて、ifの中でreturnがありますね。
returnは関数で初めて知る人が多いと思いますが、なぜifの中でreturnを使っているんでしょうか?
後で例題をもって詳しく解説しますが、先にザックリ答えを言うと「elseを省略」しています。
ifとセットでelseも覚えますが、elseを省略するケースとはどんな状況なのか見ていきましょう。
JavaScriptの条件分岐で本当にelseが必要か問題
まず以下のコードを作ってみました。
const number = {
id: null,
};
function ifFunc(number) {
if (number?.id != null) {
if (number.id < 50) {
console.log("50より小さい");
} else if (number.id < 100) {
console.log("100より小さい");
} else {
console.log("101より大きい");
}
} else {
console.log("数字ではありません");
}
}
ifFunc(number); // "数字ではありません"と出力される
numberというオブジェクトがあり、idというプロパティが格納されています。
idに何か値が入る想定で、値によって条件分岐させるような内容になっています。
上記のコードでは一旦nullを入れているので、外側の条件でelseをたどって「数字ではありません」となります。
numberのidを違う値に変えてみましょう。
const number = {
id: 10,
};
function ifFunc(number) {
if (number?.id != null) {
if (number.id < 50) {
console.log("50より小さい");
} else if (number.id < 100) {
console.log("100より小さい");
} else {
console.log("101より大きい");
}
} else {
console.log("数字ではありません");
}
}
ifFunc(number); // "50より小さい"と出力される
10を入れたので、ネストされた内側の条件分岐に入っていき「50より小さい」となりました。
もう少しやってみましょう。
const number = {
id: 80,
};
function ifFunc(number) {
if (number?.id != null) {
if (number.id < 50) {
console.log("50より小さい");
} else if (number.id < 100) {
console.log("100より小さい");
} else {
console.log("101より大きい");
}
} else {
console.log("数字ではありません");
}
}
ifFunc(number); // "100より小さい"と出力される
今度は80を入れたので、ネストされた条件分岐に入りますが最初のif文には沿わないのでelse ifの方の「100より小さい」を出力しました。
ざっくりコードの全体像が分かってきたかと思います。
正直このままで何の問題もないのですが、あえて同じ動作を別の書き方で作ってみたいと思います。
const number = {
id: null,
};
function ifFuncReturn(number) {
if (number?.id == null) return console.log("数字ではありません");
if (number.id < 50) {
console.log("50より小さい");
} else if (number.id < 100) {
console.log("100より小さい");
} else {
console.log("101より大きい");
}
}
ifFuncReturn(number); // "数字ではありません"と出力される
冒頭でお話したreturnがif文のなかにありますね。
こちらの書き方でも先ほどと同じ動作をします。
「if (number?.id == null) return console.log(“数字ではありません”);」については、「idがnullだったら”数字ではありません”と出力して終わる」という意味になります。
先ほどのコードで言うと外側のelse文の役割をしています。
ググって出てきた記事などでif文のなかでreturnを使っているのを見かけたら、「elseみたいなことしてるんだな」と思ってもらえたら良いでしょう。
先に言っておくと「elseではなくreturnで書くべき」ということではありません。
それぞれの書き方にはメリット、デメリットがあるので状況によって使い分けるのがベストです。
ではなぜif文のなかでreturnを書く人がいるのでしょうか?
大きな理由としては、
・複雑なネストを少しでも見やすくしたい
・処理を少しでも早くしたい
などです。
複雑なネストを少しでも見やすくしたい
主観もありますが、returnを使うことで少しは複雑な条件分岐が見やすくなります。
ちなみに複雑なネストを緩和するには「そもそもの条件を見直す」ということも考える癖を付けておくことをおススメします。
// returnを使わない場合
function ifFunc(number) {
if (number?.id != null) {
if (number.id < 50) {
console.log("50より小さい");
} else if (number.id < 100) {
console.log("100より小さい");
} else {
console.log("101より大きい");
}
} else {
console.log("数字ではありません");
}
}
// returnを使う場合
function ifFuncReturn(number) {
if (number?.id == null) return console.log("数字ではありません");
if (number.id < 50) {
console.log("50より小さい");
} else if (number.id < 100) {
console.log("100より小さい");
} else {
console.log("101より大きい");
}
}
少しでも処理を早くしたい
実は先ほどの2例では少しだけ処理の方法が変わっているんです。
最初に紹介した下記の場合は「総当たり検証」といって、全ての条件を網羅的に見たうえで適した条件に入り込んでいます。
// すべての条件を1周して見るような処理をしている
function ifFunc(number) {
// この条件を確認
if (number?.id != null) {
// この条件も確認
if (number.id < 50) {
console.log("50より小さい");
// この条件も確認
} else if (number.id < 100) {
console.log("100より小さい");
// この条件も確認
} else {
console.log("101より大きい");
}
// この条件も確認
} else {
console.log("数字ではありません");
}
}
そして一部をreturnに置き換えた以下の書き方だと、idがnullだった場合は”数字ではありません”を出力して後続の条件は見ずに全体の処理を完了としています。
function ifFuncReturn(number) {
if (number?.id == null) return console.log("数字ではありません");
// ↑の条件にヒットした段階で以下の処理は見ないで終了する
if (number.id < 50) {
console.log("50より小さい");
} else if (number.id < 100) {
console.log("100より小さい");
} else {
console.log("101より大きい");
}
}
正直なところ速度の違いと言ってもユーザー側で分かるような時間差はないですが、処理の内容や開発者の方針によっては「returnを使って少しでも早くしたい」という事情があったりします。
returnが混乱の種になる可能性
ちなみに先ほどのreturnを使った書き方をする人がいる一方で、「returnを書いたり書かなかったりしているのは煩雑なのでは?」という考えの方もいます。
冒頭でお話したようにreturnは関数でも登場します。
さらに処理の内容によってはreturnを省略することもできます。
コードを書いた本人は理解できていたとしても、別の開発者が引き継いでコードを確認した際に「returnがあったり無かったりするのは何か意味があるんだろうか?」と少し困惑する可能性があるんですね。
つまりreturnは使い勝手が良いが故に「書き手によって煩雑になる」という懸念点も一部では言われています。
一番簡単な対策としては以下のように「省略できるreturnを省略しない」という方法です。
function ifFuncReturn(number) {
// 大枠の条件に外れたら処理自体を終了する
if (number?.id == null) return console.log("数字ではありません");
// 戻り値がなくてもreturnは省略しない
if (number.id < 50) {
console.log("50より小さい");
return;
}
// 戻り値がなくてもreturnは省略しない
if (number.id < 100) {
console.log("100より小さい");
return;
}
//全てのifに合わなかった時のデフォルトの処理を最後に書いておく
console.log("101より大きい");
}
ネストされていた各条件を小分けにしたような構成にして、各条件について戻り値があっても無くてもreturnで終わるようにしています。
またネストされていた内側のelse文については、if文ではなく「最後に書く」ことをしていてswitch文で言うデフォルト処理のような扱いになっています。
コードの長さは変わらないですが、elseを省略した書き方ですね。
JavaScriptにおける条件分岐の書き方を3パターン紹介しましたが、自分としては作る内容によって使い勝手が変わってくると考えています。
プログラムには正解が無いので同じ処理でも人によって書き方が違うので、他人のコードを見たときにパッとくみ取れるようになるためにも今回の件は知っておくと良いかもしれません。