JavaScriptで正規表現を表す2つの方法とパフォーマンス的な違い

JavaScriptには正規表現パターンを作る方法は2つあります。

なんでわざわざ2通りの方法があるかというと用途によって使い分けるため

またどちらを使うかによってもパフォーマンス的な違いが出てきます。

そういう訳でJSでの正規表現パターンの作り方とそれぞれのパフォーマンス的な違いについて紹介

正規表現を表す2通りの方法

JavaScriptで正規表現を顕すには次の2通りの書き方が可能

  • 正規表現リテラルを使う
  • RegExpオブジェクトを使う

この2つについてそれぞれコード例何かと一緒に説明したいと思います。

正規表現リテラルを使う

これは文字通り正規表現をリテラル(定数)として記述する方法

正規表現リテラルはスクリプトのロード時にコンパイルされます。

 

その宣言方法は次のようにパターンを //  で囲むだけ

上のコードだと /\w+apple/  が正規表現リテラルになります。

ただしクォート( ' または " )で囲むとただの文字列になってしまうので注意

 

またリテラルの場合は //  の最後にgなどのオプション(フラグ)を付けることも可能です。

ちなみに利用可能なオプションについては次記事でまとめた通り

正規表現オプションの意味と使い方まとめ【JavaScript編】

オプションを使うことで複数行検索などの高度なマッチングができるようになります。

 

これを正規表現が使える関数に渡すことで検索とか置換ができます。

例えば次はmatch関数に正規表現を渡してテスト文字列を検索しているコード例

 

以上が正規表現リテラルを使ったパターンの作成方法

こちらはロード時にコンパイルされるので、変数やユーザーの入力から正規表現を作るなどの動的な作成はできません。

RegExpオブジェクトを使う方法

こちらは正規表現をRegExpオブジェクトのコンストラクタ関数を呼び出して作成する方法

正規表現リテラルと違い、RegExpオブジェクトは実行時に動的にコンパイルされます。

 

使い方はRegExpのコンストラクタ関数に文字列としてパターンを渡すだけ

正規表現リテラルと違い、パターンを //  で囲む必要はありません。

また文字列中なのでバックスラッシュ記号( \  )はエスケープする必要がある点にも注意

例えば \w  の場合は \\w  としなければ正しく動作しません。

 

またRegExpコンストラクタ関数の第2引数にgとかiとかのフラグが渡せます。

 

こちらも正規表現が使える関数に渡すことで検索や置換が可能です。

例えば次がmatch関数にRegExpオブジェクトを渡してテスト文字列を検索しているコード

 

以上がRegExpオブジェクトを使った正規表現パターンの作り方

こちらは実行時にコンパイルされるので、外部から受け取ったパターンや変数などを使って動的に正規表現を作ることが可能です。

パフォーマンス的な違い

ここまででも少し触れましたが、正規表現リテラルとRegExpでは次のような違いがあります。

  • 正規表現リテラル
    スクリプトロード時にコンパイルされる
  • RegExpオブジェクト
    コンストラクタ関数が実際に呼ばれたときにコンパイルされる

静的に実行されるか動的に実行されるかの違いがあります。

なので当然パフォーマンス的な違いも出てくるはずです。

 

では実際に本当に違いがあるのか確かめてみました。

その検証に使ったのが次のコード

100万回のループ中で正規表現リテラルまたはRegExオブジェクトを作成

そしてループ前とループ後の時間(ミリ秒)を計測して実行時間を測るという計測方法です。

 

この方法でそれぞれ10回ずつ試してみた結果がこちら

正規表現リテラル RegExpオブジェクト
1回目 10.1ms 686.7ms
2回目 10.1ms 670.4ms
3回目 10.1ms 670.1ms
4回目 10.1ms 677.8ms
5回目 9.9ms 680.2ms
6回目 10.2ms 680.8ms
7回目 10.1ms 681.0ms
8回目 10.1ms 675.4ms
9回目 10.0ms 670.7ms
10回目 8.1ms 661.2ms
平均 9.8ms 675.4ms

 

これを見ると歴然ですね。

やはりスクリプトロード時にコンパイルしている正規表現リテラルの方が圧倒的に速いです。

 

ならいつでも正規表現リテラルを使えばいいのか、というとそうでもありません。

RegExpオブジェクトは動的に実行できるでの次のような場合に役立ちます。

  • パターン内で変数を使いたいとき
  • 動的にパターンを変化させたいとき
  • 外からパターンを受け取りたいとき

正規表現リテラルはロード時にコンパイルされるので、こういうことは不可能です。

 

なので正規表現パターンを作成するときは

「コード内以外では一切変更しない静的なパターンは正規表現リテラル」
「動的に変化させる必要のあるパターンについてはRegExpオブジェクト」

のように使い分けるのがベストですね。

正規表現のデバッグ・テストに役立つツール

正規表現を使っているとデバッグやテストがしたくなることもあります。

例えば

  • どのような内部処理が行われているか知りたい
  • 無駄な処理がないかどうかデバッグしたい
  • 実際のコードで使う前に正常に動くかテストしたい

など正規表現を最適化したい場合です。

 

その場合は正規表現ができる regex101 というツールを使うのがおすすめです。

詳しくは次の記事でデバッグ方法について紹介したのでそちらをチェック

内部処理が分かりやすくステップごとに表示されるので、デバッグに役立つはずです。

ここまでのまとめ

JavaScript内での正規表現パターンの作り方についてもう一度まとめ

  • 正規表現リテラル

    次のように // パターンを囲って定義

    スクリプトロード時にコンパイルされるので一切変更しない静的なパターン作成に使うのがベスト

  • RegExpオブジェクトを使う

    次のようにRegExpオブジェクトのコンストラクタ関数を呼び出す

    実行時コンパイルされるので動的にパターンを作成したいときに使うのがベスト

動的なパターンが必要ないときはパフォーマンス的になるべく正規表現リテラルを使った方がいいですね。

 

以上JavaScriptでの正規表現の表し方についてまとめました。

ではでは($・・)/~~~