皆さんこんにちは!WESEEK ソフトウェアエンジニアの 増山 です。
今回はブラウザ上で実行される JavaScript の正規表現がテーマです。正規表現リテラルと RegExp クラスは何が違うのか、どんなメリットデメリットがあるのかについて解説します。また、ブラウザ上で実行する際の注意点についても説明します。
目次
はじめに
JavaScript の実行環境は主にサーバーサイドで使われる Node.js だったり、ブラウザで使われる Chrome の v8 engine や Safari の JavaScriptCore などがあります。
ウェブアプリケーションを開発していると特にブラウザ環境の差異によって Chrome では動くけど Safari では動かない!なんて場面に遭遇することもあります。
正規表現も、ブラウザの影響を受ける要因の一つです。今回は、JavaScript の正規表現をブラウザで使うときに筆者の 増山 が考えていること、気をつけていることを含めて書いていきます。
また、記事の最後には GROWI で実際に行われている対策についても紹介します。
JavaScript で正規表現を扱う
JavaScript で正規表現を利用する方法は主に2種類あります。
正規表現リテラル
正規表現リテラルは JavaScript の構文として用意されている書き方です。以下のように書くことで RegExp オブジェクトと同等のオブジェクトとして扱うことができます。
const regexp = /^START[1-9]{1,3}END$/;
console.log(regexp.test('START123END'));
// true
RegExp クラス
RegExp クラスは JavaScript が標準で用意している正規表現を扱うためのクラスです。以下のように初期化して使います。
const regexp = new RegExp('^START[1-9]{1,3}END$');
console.log(regexp.test('START1234END'));
// false
メリットデメリット比較
正規表現リテラルと RegExp クラスを比較します。
RegExp は動的に変更可能
RegExp クラスは初期化のときに正規表現を文字列として渡すことができます。ここでテンプレートリテラルを使用することで、動的に正規表現を変えることが可能です。一方で正規表現リテラルでは値を動的に変更できません。
let userInput = '入力';
const regexp = new RegExp(`^START${userInput}END$`);
正規表現リテラルはパフォーマンス良
正規表現リテラルで定義した正規表現はブラウザで JavaScript がロードされる際のコンパイル時に同時にコンパイルされます。逆に RegExp クラスを使用した場合は RegExp オブジェクト初期化時に正規表現がコンパイルされるため、初期化して実行するまでにコンパイル分の遅延が発生します。
よって、動的に値を変更しない場合は正規表現リテラルを使うほうがパフォーマンスが良くなります。
注意点
正規表現リテラルが JavaScript ロード時にコンパイルされるのにはデメリットも存在します。正規表現の文法が間違っていたり、ブラウザがその正規表現をサポートしていなかったりする場合はコンパイルエラーが発生します。これは JavaScript 実行前に発生するので、実行中の例外処理などで対処できません。
実際に正規表現リテラルを使用する際には、利用が想定されているブラウザで一度コンパイルしてエラーが発生しないことを確かめるようにしましょう。続けて GROWI の開発で行われている対策についてもみていきましょう。
GROWI でやってること
正規表現の一つである 後読み は Safari ~v15.6 ではサポートされていないため、正規表現をコンパイルしたときにエラーが発生します。よって正規表現リテラルの代わりに RegExp クラスを使用して、その初期化処理を try-catch で囲んで例外処理を実施します。
// 安全な正規表現
let PATTERN_DEFAULT = /^((.*)\/)?(.+)$/;https://regex101.com/r/jpZwIe/1
try {
// 後読みを含む正規表現
PATTERN_DEFAULT = new RegExp('^((.*)(?<!<)\\/)?(.+)$'); // https://regex101.com/r/HJNvMW/1
}
catch (err) {
// 例外処理
}
これでプログラムが正常に実行できます。
さらに GROWI では、開発中に間違えて危険な正規表現を混合させてしまわない様に eslint を設定しています 。
これで後読みを含む正規表現を記述した場合は lint error が出てくれるので、気づいてその場で修正できます。
まとめ
今回は JavaScript の正規表現で正規表現リテラルと RegExp クラスを使うときの違いや、ブラウザ上の JavaScript で正規表現を扱うときの注意点について触れてみました。もしコメントやご意見等ありましたら 増山の Twitter の DM にて教えてください。