JavaScript の replace で正規表現の後方参照をしたくなったのですが、その際に若干戸惑う事項があったのでメモしておきます。
経緯
何某かのフォームを作成している中で、以下のようなHTMLの構造を考えます。
<div class="form-group row"><label for="niwatari0" class="col-md-3 col-form-label text-right">ニワタリ神</label><div class="col-md-9 row"><div class="col-md-6"><select class="form-control" id="niwatari0" name="niwatari0" required><option selected>漢字を選択してください</option><option>庭渡</option><option>荷渡</option><option>見渡</option><option>三渡</option><option>水分</option><option>鬼渡</option><option>根渡</option><option>鶏</option></select></div><div class="col-md-6"><select class="form-control" id="niwatari1" name="niwatari1" required><option selected="">神格を選択してください</option><option>水神(治水・灌漑)</option><option>水神(渡航安全)</option><option>賊徒平定・鬼退治</option><option>山の神・辺り一帯を見渡す</option><option>関の神</option><option>百日咳の神</option></select></div></div></div>
性質として、
- 1ラベルに2つ以上のフィールドが横並びになる(1つの項目に2つ以上の入力というような入れ子)
- 各フィールド(今回は
select2つ)のid,nameはlabelのforと同じ名前を使用し、その後に番号を0スタートで採番している
という点が挙げられます。
このHTMLに対して、 skaterdav85/validatorjs でバリデーションを実行したいのですが、このままだと id が名称として使用されてしまします。すなわち、
選択されたniwatari0は無効です。選択されたniwatari1は無効です。
という風に表示されてしまいます。そこで、このエラーメッセージの文中の id を名前で置換したいと考えました。
結果
先に結果を記すと、以下のようなコードになりました。
//設定パラメータをオブジェクト構造で持った配列 (例)
const objConf = [
//略
{
id: 'niwatari',
nested: [
{
id: 'kanji',
'name': '漢字'
},
{
id: 'shinkaku',
'name': '神格'
}
]
}
//略
];
//validatorjs から渡されたエラーメッセージの配列 (例)
const arrayError = [
//略
['選択されたniwatari0は無効です。'],
['選択されたniwatari1は無効です。']
//略
];
for (const [key, arrayMsg] of Object.entries(arrayError)) {
//objConf から該当フォームに関するパラメータのオブジェクトのみを抽出
const conf = extractConf(objConf, key);
const reg = new RegExp(`${conf.id}(\\d)+`);
let $li = $('<li/>');
//ここが今回の肝
$li.text(arrayMsg[0].replace(reg, function (match, p1) {
return match.replace(/^(.*)$/g, conf.nested[p1].name);
}));
}
今回の注目点は replace の第二引数に関数を渡し、その中で後方参照していること。
最初は arguments を使おうと思っていたのですが、その書き方を確認しようとしたら以下の記事を見付けました。
非推奨
この機能はウェブ標準から削除されました。まだ対応しているプラウザーがあるかもしれませんが、ゆくゆくはなくなるものです。使用を避け、できれば既存のコードを更新してください。このページの下部にあるブラウザーの対応を見て判断してください。この機能は突然動作しなくなる可能性があることに注意してください。
Function.arguments – JavaScript | MDN
ただし、よく見てみると
function.argumentsは非推奨です。arguments 変数を取得するなら、より簡単に関数内で利用できる arguments オブジェクトを用いてください。
という記述もありました。
こちらはただの arguments です。
先の非推奨と記載されていたのは function.arguments なので別物のようです。
これに最初気付かずに狼狽して「 arguments 以外の書き方で」と思い、上述のやり方(関数の引数で決め打ちになっているパラメータを利用)にした、ということでした。
今回はこのやり方の方が分かりやすそうなので arguments の書き方には戻さずにそのままにしてみることにします。
備考
ちなみに、仮に今回のコードを function.arguments 使用の形で無理やり書くならば、以下のようなイメージになるでしょうか?(動作未検証)
const reg = new RegExp(`${conf.id}(\\d)+`);
function convertBackReference () {
//<関数名>.arguments の形で使用している
return convertBackReference.arguments[0].replace(/^(.*)$/g, conf.nested[reg.$1].name);
}
$li.text(arrayMsg[0].replace(reg, convertBackReference());
備考2
arguments 使用のパターンとしては以前に Node.js 側の処理で正規表現でカラーコードの場合のみダブルクーテーションを外すJavaScriptを書くという記事を書いていましたが、最初はこのコードも書き直さないとダメか、と思ったのでした。
以上、 replace の後方参照で狼狽えて違う書き方を模索した話でした。