WEB

JavaScript | 複雑な条件分岐を綺麗にする方法まとめ【if文 / 三項演算子 など】

The smug face of the author WEB
The smug face of the author
この記事は約8分で読めます。

こんにちは。株式会社インプルの加納です。

プログラミングをする上で「条件分岐」は欠かせないものですが、慣れていないとついつい実装が複雑なってしまったり、ネストの深い分岐になってしまうことがあります。

if( c <4 ) {
    if( c < 2 ) {
        if( c == 0 ) {
            // 処理
        } else {
            // 処理
        }
    } else {
        if( c == 3 ) {
            // 処理
        } else {
            // 処理
        }
    }
} else {
    // 処理
}

例えば上記のようなコードは、パッと見ただけではわかりにくいですよね。複雑な条件分岐は、書いている本人も、後からそれを読む他人も非常に疲れるものです。

そこで今回は、複雑な条件分岐をすっきり綺麗に記述するためのテクニックをまとめました。

「文」と「式」を意識して分岐を整理する

JavaScriptには「文」「式」があります。
この2つの違いを意識することで、複雑な分岐を整理することができます。

  • 「文」は、マシンへの命令です。
  • 「式」は数学的な値です。

プログラミングにおいて、私たちが「文」を読む時は「フロー」を意識しなければなりません

if - else は文

トロッコと分岐

if - else は文です。 つまりフローになります。例えるならトロッコに乗るように、線路に沿って進んだ先に分岐があります。

では何が辛いかというと、トロッコにずっと乗ってないといけないのが辛いわけです。

条件を確認して、あっちへ行ったりこっちへ行ったり、時には線路を戻って(アレ? なんの条件にしたがってたんだっけ?)となったり。

常に頭の中でフローを意識しなければならないのは、かなり体力を使います。

if - else 文を 式に書き換える

複雑な条件も、少しの工夫で読みやすく、また書き換えやすくできます。

文ではなく、式で表現することで、簡潔にできることが多々あります。具体的には、以下の2点を意識するとよいでしょう。

  • フローに乗る前に、必要な処理やデータはあらかじめ用意する
  • フローの中で、あまりゴチャゴチャやらない

1. とっとと return する

条件反射的にでelseを書かずに、returnすることで見通しがよくなることがあります。

before

const checkConnection = (isOnline) => {
  if (isOnline) {
    // オンラインの場合の処理
    makeReservation();
    ...
    ...
  } else {
    // オフラインの場合の処理
    alert('オフラインです');
  }
}

after

const checkConnection = (isOnline) => {
  if (!isOnline) {
    alert('オフラインです');
    return;
  }
  // オンラインの場合の処理
  makeReservation();
  ...
  ...
}

2. 三項演算子を使う

三項演算子は「値の場合分け」です。
式の中で値の場合分けをしているだけで、if文によるフローの分岐はありません。

before

const checkAge = (age) => {
  if (age > 18) {
    console.log('大人です');
  } else {
    console.log('子供です');
  }
}

after

const message = age > 18 ? '大人です' : '子供です';
console.log(message);

ifに比べて読みづらい場合は、改行すれば良いかと思います。

after

const message = age > 18
  ? '大人です'
  : '子供です';
console.log(message);

3. 関数にまとめる

大きな処理のかたまりは、関数に分割します。

before

function drinkAlcohol(isAdult, drink) {
  if (!isAdult) {
    return;
  }

  if (drink === 'beer') {
      eatSomething();
      adult.enjoyDrink(drink);
      utils.goToBathroom(2);
  } else {
      adult.goShopping();
      utils.buyDrink('beer');
      startCooking();
  }
}

after

function drinkAlcohol(isAdult, drink) {
  if (!isAdult) return;

  const enjoyBeer = () => {
    eatSomething();
    adult.enjoyDrink(drink);
    utils.goToBathroom(2);
  }

  const getBeer = () => {
    adult.goShopping();
    utils.buyDrink('beer');
    startCooking();
  }

  drink === 'beer' ? enjoyBeer() : getBeer();
}

※ 関数の呼び出しを三項演算子に書くのは辞めておいたほうが良いという意見もあります。
最初は良くても改修時に引数が増えたり、長い関数の呼び出しを三項演算子に書いてしまい、逆に理解しずらくなってしまうことがあるからです。
あくまでも、シンプルな使用に留めた方が良いでしょう。

4. オブジェクト型を使う

オブジェクトとして、必要な条件としてのキーと、処理内容をあらかじめ宣言しておき、
必要に応じて取り出して使います。

before

const calc = {
    run: function(op, n1, n2) {
        let result;
        if (op == "add") {
            result = n1 + n2;
        } else if (op == "sub" ) {
            result = n1 - n2;
        } else if (op == "mul" ) {
            result = n1 * n2;
        } else if (op == "div" ) {
            result = n1 / n2;
        }
        return result;
    }
}

calc.run("sub", 5, 3); //2

after

const calc = {
  add: (a, b) => a + b,
  sub: (a, b) => a - b,
  div: (a, b) => a / b,
  mul: (a, b) => a * b,
  run: (fn, a, b) => fn(a, b),
};

calc.run(calc.mul, 7, 4); //28

コレは、switch - caseの書き換えにも使えます。
(ただし、パフォーマンスに劣るという意見もあるようです。個人的には、break;を何度も書かなくていいので好きです)

まとめ

文ではなく、式で表現することで、簡潔にできることが多々あるようです。

  • フローに乗る前に、必要な処理やデータをあらかじめ用意する
  • フローの中で、あまりゴチャゴチャやらない

文の方が読みやすい場合もある

当然ですが、常に三項演算子を使うべき!のような極端なお話ではありませんのでご注意ください。

基本的には通常の if文 が誰にとっても分かりやすい条件分岐になると思います。ただし、あまりにもネストが深くなったり、読みづらいと感じる場合にはここで紹介した内容を検討してみると良いかもしれません。

いろいろなバリエーションを使いこなして、見通しの良い記述をしたいですね。

参考

http://ichitcltk.hustle.ne.jp/gudon2/index.php?pageType=file&id=word_short_circuit.md
https://medium.com/swlh/5-ways-to-replace-if-else-statements-857c0ff19357
https://dev.to/damxipo/avoid-use-if-on-our-js-scripts-1b95]
https://jp.quora.com/JavaScript-no-shiki-to-bun-no-chigai-ha-nande-shou-ka/answers/172891992?ch=10&share=5c48a6c6&srid=NGuuH
https://dev.to/mortoray/why-switch-is-better-than-if-else-9cj
https://qiita.com/dojyorin/items/6bc198d3287991e9153d