🐡

特定のパスからのアクセスか判定したい&RegExpとリテラルの差について(JS:正規表現)

2023/03/16に公開

Javascript(以下JS)で特定のパスからのアクセスか判定したい場合がありました。正規表現で対応しようと考えたのですが、意外に悩まされたので、簡単にまとめておこうと思います。完成したコードがこちら。

let pathname = '/index/5/edit/'
if (new RegExp(/\/index\/.*\/edit/).test(pathname)) {
console.debug('該当のURLからのアクセスです。');
}

ここでは「/index/5/edit/」というパスからきた場合、debugで「該当のURLからのアクセスです。」という文言を表示させるようにしています。パスに含まれる5ですが、ここではユーザーIDとして、この部分は動的に変わることを想定しています。それでは正規表現の解説をしていきます。

// ① オブジェクトを生成して、行う方法
new RegExp(/\/index\/.*\/edit/)

// ② 正規表現リテラルで行う方法
let literal = /\/index\/.*\/edit/

JSで正規表現を行う為には2通りの方法があり、1つは上記のようにRegExpオブジェクトを作成して行う方法。もう一つは正規表現リテラルです。前者は正規表現を扱うための標準組み込みオブジェクトを指し、後者はJSで用意されている正規表現を行う為の構文です。どちらの場合でも今回のケースは対応できますが、両者には少し違いがありその点については後半に説明を行い、ここではコードの解説をしていきたいと思います。

new RegExp(/\/index\/.*\/edit/)

他言語でも同じような形式かと思いますが、正規表現を行う宣言としてスラッシュを最初と最後に書きます。そしてパスにはスラッシュが含まれていますが、正規表現では上記のように宣言を意味するので、スラッシュの前にバックスラッシュを入れることで、通常の文字として認識(エスケープ)させるようにします。indexやeditのようにエスケープさせる必要のない通常の文字列はそのまま記載します。間にある.*ですが、ドットは改行以外の任意の1文字にマッチするかを判定し、アスタは直前の文字が0回以上繰り返しにマッチするかを判定します。アスタがなしでドットだけにしてまうと、2文字以上が来た場合、次の記号とぶつかってしまう為組み合わせて使用しています。

new RegExp(/\/index\/.*\/edit/).test(pathname)

最後のtest()という部分ですが、こちらは正規表現の為に用意されているメソッドで()の内の引数に渡した値が、正規表現のパターンにマッチすれば「true」を返し、マッチしなければ「false」を返します。これをif分で囲ってやることで、debugを出す・出さないを制御している訳ですね。

RegExpオブジェクトと正規表現リテラルを使用した場合の違い

両者の違いについて、コンパイルされるタイミングが異なるという点が挙げられます。RegExpオブジェクトはコンストラクタ関数が実際に呼ばれたときにコンパイルされるのに対し、正規表現リテラルを使用した場合はスクリプトロード時にコンパイルされます。これがどういう差を生み出すかというと以下の2つが挙げられます。

  • パフォーマンス(実行時間)に差が出る

  • 動的に正規表現の中身を変えられるか

パフォーマンスについてですが、正規表現リテラルの場合スクリプトロード時にコンパイルされるので、RegExpオブジェクトと比較するとかなり速いです。では常に正規表現リテラルを使用した方が良いんじゃないかというとそうではなく、動的に正規表現の中身を変えたい場合はRegExpオブジェクトを使用する必要があります。

// ユーザーが実際に入力した値を受け取ることを想定(動的に値が変わる)
let userInput = 5

let pathname = '/index/5/edit/'
if (new RegExp(`/index/${userInput}/edit`).test(pathname)) {
console.debug('該当のURLからのアクセスです。');
}

上記のようにuserInputを変数として、実際にユーザーが入力した値がパスに入ることを想定した場合、ここを厳密に正規表現をかけようとすると正規表現の中身が動的に変わりますので、このように式展開をする形になります。RegExpオブジェクトはコンストラクタ関数が実際に呼ばれたときにコンパイルされる為このような実装ができますが、正規表現リテラルを使用した場合はスクリプトロード時にコンパイルされる為動的に変わる値を受け取れないということになるわけです。また正規表現リテラルでは式展開をすると単純な文字列に扱いになってしまう為、そもそもこういう書き方ができないというのもあります。

終わりに

以上簡単ではありますが、JSの正規表現についてまとめてみました。特にRegExpオブジェクトと正規表現リテラルを使用した場合のパフォーマンスの違いについては、実装時には考えてもいなかったので良い学びになりました。今回の例の他にも、様々な正規表現のパターンがあると思いますので、機会があればチャレンジしていきたいですね。

Discussion