はじめに
JSに関する小ネタです。
以下のように感じる人はもしかしたらこの記事を読む価値があるかもしれません。
-
if-else
の羅列はやっぱ読みづらいし面倒だなー -
switch
文でパターンマッチみたいなの使いたいなー
ただしパターンマッチを実現するという内容の記事ではないので期待はしすぎないでください。
注意
- サンプルコードはNode.jsの10.13.0で試してます
- ほとんどのブラウザで使える内容だとは思いますが、検証しきれていません
- 間違ってるとこがありましたら指摘お願いします
switch
文が使いにくいと感じる場面
"/hoge/fuga"
のような URL パスっぽいものがあったときに、その値に応じて処理や返り値を変えたいとする。
単純に完全一致だけで扱えるケースであればswitch
文を使える。
function returnSomething(path) {
switch (path) {
case '/': {
return 'top';
}
case '/hoge': {
return 'hoge';
}
case '/fuga': {
return 'fuga';
}
case '/hoge/fuga': {
return 'hoga';
}
case '/hoge/piyo': {
return 'hoyo';
}
default: {
return 'anything else';
}
}
}
// 読み飛ばしてもよいコメント
// case にわざわざ "{}" をつけていますが、この例では必要ありません。
// ただし変数の名前空間を分けることができるので、つけとくと便利な場面もあります。
だけど完全一致だけじゃない場合はよくあると思う。(前方一致とかワイルドカードみたいなことをしたい)
そんなとき今までなら諦めてif-else
を使ってました。
function returnSomething(path) {
if (path === '/') {
return 'top';
} else if (path === '/hoge') {
return 'hoge';
} else if (path === '/fuga') {
return 'fuga';
} else if (path.startsWith('/hoge/')) {
return 'hogex';
} else {
return 'anything else';
}
}
(String.prototype.startsWith()
は IE11 では使えません。polyfill とかでなんとかすると良いと思います。)
if
とelse if
でalignmentがずれたり、path ===
と何回も書いたり。
個人的にはこういう読みづらくミスを誘発しやすいものを避けたい。
もちろんcase
を連続で並べる方法も数が少ない場合は良いですが、
数が多くなったりワイルドカードみたいなものを実現したいときには現実的な対応策になりません。
// (省略)
case '/hoge/fuga':
case '/hoge/piyo': {
return 'hogex';
}
// (省略)
そんなときに同僚のコードレビューしていたら、とても面白いコードができあがったのでそれを紹介します。
switch
文をちょっと工夫して使う
上記のif-else
の例をswitch
で実現したものを先に示しときます。
function returnSomething(path) {
switch (path) {
case '/': {
return 'top';
}
case '/hoge': {
return 'hoge';
}
case '/fuga': {
return 'fuga';
}
case path.startsWith('/hoge/') && path: {
return 'hogex';
}
default: {
return 'anything else';
}
}
}
正直if-else
と大差ないとか思うかもしれないですが、まあマシになったということにしときます。
あとはcase path.startsWith('/hoge/') && path:
の動きを理解するだけです。
ちょっとした解説
-
path.startsWith('/hoge/')
はBoolean
を返すので、true
とfalse
という具体的な値で考えてみる -
path.startsWith('/hoge/') && path
という式は以下のようになる
-
true && path
:path
を返す -
false && path
false
を返す
-
switch (path)
としているので当然比較は以下のようになる
-
path === path
: これはNaN
以外なら必ず成り立つ -
path === false
: これはpath
がfalse
でない限りは成り立たない
つまり真偽値 && path
とすることで"自分自身"と、もしくは"真偽値の偽"との比較が最終的に行われるということでした。
なのでcase path.match(/^\/hoge\/.*/) && path:
みたいなこともできます。
ただしswitch
に渡す変数次第で、"真偽値の偽"との比較が真になってしまうこともあるため、
注意して使用したほうが良さそうです。
蛇足
let
で変数をあらかじめ宣言しておいて、case
で代入も行うという荒業を使えば、
正規表現の結果を利用するといったことも可能です。
(が、個人的にはあんまり多用したくない印象)
function returnSomething(path) {
let matched;
switch (path) {
case '/': {
return 'top';
}
case '/hoge': {
return 'hoge';
}
case (matched = path.match(/\/hoge\/(.*)/)) && path: {
return `hoge + ${matched[1]}`;
}
default: {
return 'anything else';
}
}
}
※ IE では let
を使えないのでご注意ください。
さいごに
今回はswitch
文の少し変わった使い方を紹介しました。
はやくパターンマッチが正式導入されるといいですね。
明日は @forl_head_officer さんの記事です!