🎄

ENCA 23日目: RegExp.prototype.replace のキャプチャで2桁から1桁にフォールバックする仕様修正

2024/12/24に公開

RegExp.prototype.replace と正規表現

文字列を置換する際に用いる RegExp.prototype.replace は第一引数に正規表現[1]を受け取ることが出来ます。その場合第二引数には文字列か函数を渡すことでマッチした部分文字列を置き換えます。文字列を使った例が以下のようになります。

console.log("foo".replace(/foo/u, "bar")); // "bar"
console.log("foo".replace(/(foo)/u, "$1 $1")); // "foo foo"
console.log("foo".replace(/(?<char>o)/u, " $<char> ")); // "f o o"

さて $1 のように正規表現のキャプチャにマッチした部分を置換する場合、そもそも正規表現側に対応するキャプチャがない場合はそのまま $1 のような文字列が結果に残ります。

console.log("foo".replace(/(foo)/u, "$1 $2")); // "foo $2"

RegExp.prototype.replace のキャプチャで2桁から1桁にフォールバックする仕様修正

RegExp.prototype.replace の第二引数に $12 のように2桁のキャプチャ置換を指定することを考えます。この場合正規表現側のキャプチャ数によって以下のような結果になります。

// 正規表現に12個のキャプチャがあるケース
// 12番目の "J" がキャプチャされ "$12" を "J" に置換する
console.log("ABCDEFGHIJ".replace(/(A)(B)(C)(D)(E)(F)(G)(H)(I)(J)/, "$12"));
// => "J"

// 正規表現に11個のキャプチャがあるケース
// 1番目の "A" がキャプチャされ "$1" を "A" に置換する
console.log("ABCDEFGHIJ".replace(/(A)(B)(C)(D)(E)(F)(G)(H)(I)J/, "$12"));
// => "A2"

どのブラウザランタイムでもこのように2桁から1桁にフォールバックする挙動となっていましたが、仕様ではそのようなフォールバックについて記述されていませんでした。2023年9月に仕様を実態(Web Reality)に合わせようと議論され、承認されました。

https://github.com/tc39/ecma262/pull/3157

脚注
  1. 実はネイティブの RegExp でなくても Symbol.replace メソッドを持っていればそちらが実行されます。 ↩︎

Discussion