ご存知のように switch文は危険がいっぱいです。
別に気を付ければいいだけだけど、使わない方がメリットはどちらかと言えば多めです。
(もちろんswitch文にもメリットはあるけど、やっぱりできれば使いたくない)
そこで JavaScript での switch文の代わりを本気で考えてみました。
このページの目次
switch文は break; 付け忘れリスクがあって危険
なぜ switch文 を使うのが危ないのか・・・
それは単純に break; 付け忘れのリスクがあるから。
+ あとPHPとかだと厳密な型チェックでないのも問題あり。
もし break; をつけないとエラーになるんなら、まだ救いようがあります。
でも switch文 でそういう機能がついている言語はないから、必ずヒューマンエラーが発生してしまいますね。ほおっておくと深刻なバグの温床になるかもしれなくて怖い
例えば危険なコードはこんな感じ
▼ break付け忘れのヤバい例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var which = 'hoge'; switch(which){ case 'hoge': { alert('hogehoge'); /// breakしてない } case 'fuga':{ alert('fugafuga'); break; } case 'piyo': default:{ alert('piyopiyo'); break; } } |
このコードだと1つめのcase文の中身が実行された後に、 break; がついてないから 2つ目のcase分まで実行されてしまうって訳です。
つまり 1."hogehoge" のアラート => 2."fugafuga" のアラート という意図しない結果に、、、
これは気を付けてれば起こらないけど、疲れて仕方ない時はついやってしまいがちです。
これを回避する方法を真面目に考えてみました。
代替案1.複数のif文に置き換える
1つめは複数のif文に素直に置き換える方法
可読性は落ちるけど、変なバグが残るリスクよりはましという考え方です。
▼ 先ほどの例ならこういう感じ
1 2 3 4 5 6 7 8 9 10 11 |
var which = 'hoge'; if(which == 'hoge'){ alert('hogehoge'); } else if(which == 'fuga'){ alert('fugafuga'); } else { alert('piyopiyo'); } |
う~~~ん、、、、、、
まあ見ての通りあんまりスマートじゃありません。
あとネストが1段階深くなってしまうのも少し嫌な感じもします。
でももし初心者ならトリッキーなことせず、こういう素直な書き方がいいかもしれないです。
代替案2.関数オブジェクトの配列を使うやり方
お次はコレ
関数オブジェクト配列を使って switch文 を再現する方法
JavaScriptの場合、実は 関数も組み込み型 として扱われています。
▼ 関数についてMDNでの解説
関数 は、他のコードや自分自身、関数を参照する 変数 から呼ばれるコード断片です。関数が呼ばれると、引数 が関数への入力として渡され、次に関数は任意で出力を戻します。JavaScript における関数は、オブジェクト でもあります。
引用元 : https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Functions
▼ 実は全部 Function オブジェクトだった
JavaScript におけるすべての関数は、実際には Function オブジェクトです。
引用元 : https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function
悪名高い(?)C言語のポインタ的な仕組みかと勘違いしてた・・・
でも関数を作成した時点で
Function というオブジェクトになるみたいですね。
その利点は 値と関数を結びつけた連想配列も作れるということ
これを使えば switch文 なんて使わなくても、スマートに条件分岐できます。
たとえば関数の連想配列を作った switch文 の代わりの書き方は次の通り
▼ 先ほどのコードを関数配列で書いてみた
1 2 3 4 5 6 7 8 9 10 11 |
var which = 'hoge'; const funcs = { 'hoge': function(){ alert('hogehoge'); }, 'fuga': function(){ alert('fugafuga'); }, 'piyo': function(){ alert('piyopiyo'); } } if(funcs[which] != null){ funcs[which](); } |
この方法だと break; の付け忘れなんて意識しなくていいし、関数オブジェクトがあるかどうかだけに注意しておけばかなりスマートに書けますね。
複雑な処理が必要ないなら、この書き方が一番な気がする
ちなみに実行する関数オブジェクトには引数も渡せます。
▼ bindメソッドを使って引数を渡す例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var which = 'hoge'; const funcs = { 'hoge': showAlert.bind(this, 'hogehoge'), 'fuga': showAlert.bind(this, 'fugafuga'), 'piyo': showAlert.bind(this, 'piyopiyo') } function showAlert(msg){ alert(msg); } if(funcs[which] != null){ funcs[which](); } |
bindメソッドの第1引数には this にしたいオブジェクト、その後に実行関数の引数をずらずらと渡すことが可能です。あとは好きなタイミングで呼び出せばOK
ここまでのまとめ - 関数配列を使うのが一番スマート(?)
最後に今までの方法をまとめ
- 代替案1.複数のif文に置き換える
あんまりスマートじゃないけど、break; 付け忘れは回避できる。それだけ
- 代替案2.関数オブジェクトの配列を使うやり方
関数は Function オブジェクトなので連想配列化できる。判定したい値と実行したい関数の連想配列を作るだけでいいのがスマート
個人的には2番目が一番スマートだと思います、
もし他にもっとスマートな方法があれば教えてください。ではまた($・・)/~~~