変数構文

【JavaScript】 3項演算子とは?使い方と意外とハマる落とし穴

更新日:2023/10/17

ある決まった条件のとき、3項演算子はif-elseよりも簡潔にコードを書くことができます。
JavaScriptで初めて3項演算子を知った僕は、猿のように3項演算子を使いまくりました。
でもたまーにハマるのです。

そこで今回は、JavaScriptの3項演算子の使い方と、注意したい点をお伝えします。


2023/10 表現を少し変更しました

 

3項演算子とは

3項演算子は、式の結果が真(true)か偽(false)かを判定し、その結果で真と偽各々に用意された式の結果を返す演算子です。

3項演算子は条件演算子とも呼ばれています。

3項演算子の構文は、次のようになります。

条件式  ?  条件式がのとき評価する式  :  条件式がのとき評価する式

次のように使用します。

const a = 100;
const b = a === 100 ? "100です" : "100ではない";
console.log( b );
>> "100です"

変数a が 100 なら "100です" が、変数b に代入されます。
100 でないなら "100ではない" が、代入されます。

この例では a100 なので、b"100です" になります。

僕の場合3項演算子を知らない頃は、if-elseを使って、次のように書いていました。

let b;
if( a === 100 ){ b = "100です";}
else { b = "100ではない"; }
console.log( b );
>> "100です"

3項演算子の方が簡潔な印象がありますね。

また重要なのが、3項演算子は変数bconstで定義できている点です。

僕の場合は、letよりもconstを使うケースが多いので、とても役立つ演算子です。

constについては、次のページを読んでみてください。
【JavaScript】変数宣言var/let/constの使い分け
条件式については、次のページを読んでみてください。
【JavaScript】if文の使い方と条件の種類

 

3項演算子の使用例

3項演算子の使用例をいくつか挙げてみます。

カンマで複雑な処理をおこなう【非推奨】

いきなり非推奨ですが、カンマを使うことで複雑な処理を行えます。

次のコードは変数の値がnullの時に他の値で初期値をセットしています。

let a = null , b = null;
const result =  a !== null  
          ? ( b === null ? b = 2 : null  )
          : (a = 5 , b = 2  );

console.log( a );
>> 5
console.log( b );
>> 2
console.log( result );
>> 2

3項演算子の評価する式は一つだけしか記述できませんが、( )と カンマ式( , ) を使用することで、複数の式を一つの式とみなすことができます。
そして最後の式の結果が、()の結果となります。
上記のコードでは b = 2 の結果である 2 が、result に代入されています。

ただしカンマ式はifやforなどの制御文、constやletなどの変数宣言は使用できません。
returnも使用できないので、どの値が式の結果になるのかを正確に把握しておく必要があり、少し勘違いするだけで悲惨な結末になります。

初心者には少しかっこいいコードに見えるかもしれませんが、保守性などを考慮すると推奨できません。

上記コードの次の3項演算子は、条件が偽の時に何もしないという意味でnullを返しています。

( b === null ? b = 2 : null )

少し無駄な印象があります。Null合体演算子を使用すると無駄を省くことができます。

( b ?? ( b = 2 ) )

Null合体演算子については、次のページを読んでみてください。
【JavaScript】 Nullishな値に関する演算 Null合体演算子とオプショナルチェーン演算子

関数を呼び出す

複雑な処理を行いたいときはカンマではなくて、関数を呼び出す方がわかりやすいです。

const func1 = () => 1;
const func2 = ( param ) => {
    return param === 2 ?  param * 2 : -1;
};

const result =  false ? func1() : func2( 2 );

console.log( result ); // 4

即時関数を呼び出す

関数の代わりに、即時関数を使用することができます。

const a = false  
    ? (()=>{ return 1; })() 
    :  (()=>{ return 2; })();

console.log( a ); // 2

参考:【JavaScript】 即時関数の挙動について調べてみた
参考:【JavaScript】 アロー関数は何者!?かっこいいだけじゃない!

複数の変数に代入する

分割代入を使うと、複数の変数に代入できます。

const [a , b , c] = true ? [ 1 , 2 , 3] : [ "a" , "b" , "c" ]; 

console.log( a ); // 1
console.log( b ); // 2
console.log( c ); // 3

3項演算子で複数の結果を返したいときは、非常に便利な仕組みです。

分割代入構文については、次のページを見てください。
【JavaScript】 分割代入はどこが便利なのか

異なる変数に代入する

カンマで複雑な処理をおこなう【非推奨】で既に取り上げていますが、式中で任意の変数に値を代入することができます。

let a = 0 , b = 0;
const c = a === 0 ? a = 5 : b = 10;

console.log( a ); // 5
console.log( b ); // 0
console.log( c ); // 5

代入した結果は式の結果となり、3項演算子の結果となります。

備考:

次のようなコードがあるとして、

z = true ? (a = 5 , b = 10 ,a * b) : a * b

(a = 5 , b = 10 ,a * b)は、一つの演算式です。結果は50になります。

※カンマ演算子は長すぎるのはよくないけれど、これくらいならいいんじゃないかと思う…

 

3項演算子の入れ子(ネスト)

3項演算子はネスト(入れ子)にできます。

3項演算子ネストの例

例えば次のようなif-elseのコードがあるとします。

if( x < 10 ) { a = 1;} // (1)
else if( x < 20 ) { a = 2; } // (2)
else if( x < 30 ) { a = 3; } // (3)
else { a = 4; }  // (4)

これを3項演算子に書き換えてみます。

まず最初の(1)を3項演算子へ書き換え。

a = x < 10 ? 1 : falseのときの式

次に(2)をfalseのときの式の位置に、3項演算子で記述。

a = x < 10 ? 1 : x < 20 ? 2 : falseのときの式2

次に(3)をfalseのときの式2の位置に、3項演算子で記述。

a = x < 10 ? 1 : x < 20 ? 2 : x < 30 ? 3 : falseのときの式3

最後に(4)はifがないので、falseのときの式3に値だけ記述。

a = x < 10 ? 1 : x < 20 ? 2 : x < 30 ? 3 : 4

非常にわかりにくいですね。

各所からクレームがくるので、改行をいれて読みやすくします。

a = x < 10 ? 1
      : x < 20 ? 2
      : x < 30 ? 3
      : 4;

これなら、わかりやすいですね。
よく見ると、if-elseの順番に並んでいるだけなのがわかります。

実際に想定通りに動くのか試してみましょう。

x =

動きましたね?

他言語でのネスト使用は注意!

ほとんどの言語は、上の例が想定通りに動作してくれます。
しかしPHPなどの一部の言語は、思いもよらぬ結果となります。

次のPHPコードは、$aの値が2になることを想定しています。

PHP

$x = 15;
$a = $x < 10 ? 1
        : $x < 20 ? 2
        : $x < 30 ? 3
        : 4;
echo $a;  // 3

しかし結果は3でした。

理由は、JavaScriptとPHPでは式の解読(評価)の方法がことなるからです。

JavaScriptは右から順番に解読していきます。
( )で優先順位を表してみます。

a = ( x < 10 ? 1 : ( x < 20 ? 2 : ( x < 30 ? 3 : 4 ) ) ) 

最初に x < 30 ? 3 : 4 を、次に x < 20 ? 2 : ( x < 30 ? 3 : 4 ) を評価していくわけです。

一方PHPは左から評価していきます。

$a = ( ( ( $x < 10 ? 1: $x < 20 ) ? 2 : $x < 30 ) ? 3 : 4; )

つまり $x < 10 ? 1: $x < 20 が最初に評価されます。

$xが15のときの流れ

1. 『 $x < 10 ? 1: $x < 20 』  $x < 20 → true が結果として採用される

2 『 true  ? 2 : $x < 30 』 2 が結果として採用される

3 『 2 ? 3 : 4 』 3 が結果として採用される

3項演算子はJavaScript以外にも採用されています。
しかし同じコードでも結果が異なることがあります。

別の言語でプログラムを始めたとき、おそらく評価順のことは忘れていると思います。
そして想定通りの動作をしなくて途方に暮れるのです。

そんなことにならないように、『( )を使って優先順位を明示しておく』『そもそもネストを使わない』などのクセをつけておいたほうがいいと思います。

 

3項演算子でハマるパターン

僕は理由がわかっているのに、いつも次のような失敗をやってしまいます。

const ameFlg = true;
const message = "今日は" + ameFlg ? "雨" : "晴れ";
console.log( message );

今日は雨』と表示してほしいコードです。

しかし結果は『』です。
今日は』が消えてしまいました。

なぜでしょう?

答えは、こちら。

const message = ( "今日は" + ameFlg ) ? "雨" : "晴れ";

『今日は" + ameFlg』が、条件式となってしまっているのです。

他言語でのネスト使用は注意!で、ネストした場合右から評価されると書きました。
そのため、次の例のように錯覚することがあります。

const a = "今日は" + ( ameFlg ? "雨" : "晴れ");

しかしそうではないので、注意が必要ですね。

なお対応策は、上の例のように ( ) をつけることです。

僕はわかっていてもやってしまうので、少しでも複雑な3項演算子は ( ) で囲っています

 

まとめ

3項演算子はif-elseを簡潔に書くことができて、見た目にもちょっとかっこよいですね。

でも多用するのが嫌いな人も多いので、企業プログラマさんはほどほどにしましょう。

また ( ) の付け忘れで結果が大きく変わってしまうケースもあります。

プログラム規模が大きくなると問題を発見するのに非常に苦労するので、僕みたいなおっちょこちょいは単純な3項演算子でも ( )をつけておいたほうが安全です。

※所属企業のコーディング規約でダメって言われてたら仕方ないけどね…

更新日:2023/10/17

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。