Legacy RegExp features in JavaScript S3
中文标题:JavaScript中的遗留RegExp特性
- 阶段: Stage 3
- 状态: 进行中
- ECMAScript 版本: —
- 同步时间: 2026年8月26日
- English original · 官方仓库
该提案规定了遗留的RegExp特性,包括像RegExp.$1这样的静态属性和RegExp.prototype.compile方法,以维护web兼容性。它定义了这些静态属性如何在成功匹配后更新,并且可以被删除以保证安全。它还提议对RegExp的子类和跨领域的regexp禁用这些遗留特性。
以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。
JavaScript中的遗留RegExp特性
当前状态
ECMAScript提案,处于流程的第3阶段,参见 https://github.com/tc39/proposals
简介
这是JavaScript中遗留(已弃用)RegExp特性的规范草案,即构造函数的静态属性如 RegExp.$1 以及 RegExp.prototype.compile 方法。
这并不反映实现的做法,而是编辑认为为了维护web兼容性它们应当做的最不坏的事情。
RegExp静态属性(目前不属于ECMA 262的一部分,见 tc39/ecma262#137)被规定如下:
- 这些属性返回的值在每次成功匹配后被更新。
- 它们可以被删除。(这对于希望避免全局副作用的的安全环境很重要。)
该提案还包括另一个特性,在成为规范之前需要达成共识和实现经验:
- RegExp遗留静态属性以及
RegExp.prototype.compile对于RegExp的真正子类的实例以及跨领域的regexp被禁用。参见这里的详细动机。
我们已经尝试识别该特性引入的向后兼容性破坏可能带来的潜在风险。
这些修订相对于在 https://tc39.github.io/ecma262/ 找到的最新ECMAScript规范草案。 对现有算法的更改以粗体标记。
所有修订都属于附录B,包括那些修改规范其他部分定义的对象或算法的修订。
%RegExp%
%RegExp% 内部对象,即内置的RegExp构造函数,具有以下额外的内部插槽:
- [[RegExpInput]]
- [[RegExpLastMatch]]
- [[RegExpLastParen]]
- [[RegExpLeftContext]]
- [[RegExpRightContext]]
- [[RegExpParen1]]
- [[RegExpParen2]]
- [[RegExpParen3]]
- [[RegExpParen4]]
- [[RegExpParen5]]
- [[RegExpParen6]]
- [[RegExpParen7]]
- [[RegExpParen8]]
- [[RegExpParen9]]
所有这些内部插槽的初始值是空字符串。
RegExpAlloc ( newTarget )
RegExp实例有一个额外的插槽,可选地保留对其构造函数的引用。它用于决定该regexp是否启用非标准的遗留特性。RegExpAlloc抽象操作修改如下:
- 让 obj 为 ? OrdinaryCreateFromConstructor(newTarget, "%RegExpPrototype%", «[[RegExpMatcher]], [[OriginalSource]], [[OriginalFlags]], [[Realm]], [[LegacyFeaturesEnabled]]»)。
- 让 thisRealm 为当前 Realm 记录。
- 将 obj 的 [[Realm]] 内部插槽的值设置为 thisRealm。
- 如果 SameValue(newTarget, thisRealm.[[Intrinsics]].[[%RegExp%]]) 为 true,则
- 将 obj 的 [[LegacyFeaturesEnabled]] 内部插槽的值设置为 true。
- 否则,
- 将 obj 的 [[LegacyFeaturesEnabled]] 内部插槽的值设置为 false。
- 执行 ! DefinePropertyOrThrow(obj, "lastIndex", PropertyDescriptor {[[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false})。
- 返回 obj。
RegExpBuiltInExec ( R, S )
在 RegExpBuiltInExec 抽象操作中,添加了一个钩子,用于在成功匹配后更新 %RegExp% 的静态属性。算法的最后三步修改如下:
- ...
- (当前步骤23)执行 ! CreateDataProperty(A, "0", matchedSubstr)。
- 让 capturedValues 为一个新的空 List。
- (当前步骤24)对于每个整数 i 使得 i > 0 且 i ≤ n:
- ...
- (当前步骤24.e)执行 ! CreateDataProperty(A, ToString(i) , capturedValue)。
- 将 capturedValue 追加到 capturedValues 的末尾。
- 让 thisRealm 为当前 Realm 记录。
- 让 rRealm 为 R 的 [[Realm]] 内部插槽的值。
- 如果 SameValue(thisRealm, rRealm) 为 true,则
- 如果 R 的 [[LegacyFeaturesEnabled]] 内部插槽的值为 true,则
- 执行 UpdateLegacyRegExpStaticProperties(%RegExp%, S, lastIndex, e, capturedValues)。
- 否则,
- 执行 InvalidateLegacyRegExpStaticProperties(%RegExp%)。
- 如果 R 的 [[LegacyFeaturesEnabled]] 内部插槽的值为 true,则
- (当前步骤25)返回 A。
UpdateLegacyRegExpStaticProperties ( C, S, startIndex, endIndex, capturedValues )
抽象操作 UpdateLegacyRegExpStaticProperties 在成功匹配后更新 %RegExp% 的静态属性的值。
- 断言:C 是拥有 [[RegExpInput]] 内部插槽的对象。
- 断言:Type(S) 是 String。
- 让 len 为 S 中的代码单元数量。
- 断言:startIndex 和 endIndex 是整数,且 0 ≤ startIndex ≤ endIndex ≤ len。
- 断言:capturedValues 是字符串列表。
- 让 n 为 capturedValues 中的元素数量。
- 将 C 的 [[RegExpInput]] 内部插槽的值设置为 S。
- 将 C 的 [[RegExpLastMatch]] 内部插槽的值设置为一个字符串,其长度为 endIndex - startIndex,包含 S 中索引从 startIndex 到 endIndex - 1 的代码单元,按升序排列。
- 如果 n > 0,则将 C 的 [[RegExpLastParen]] 内部插槽的值设置为 capturedValues 的最后一个元素。
- 否则,将 C 的 [[RegExpLastParen]] 内部插槽的值设置为空字符串。
- 将 C 的 [[RegExpLeftContext]] 内部插槽的值设置为一个字符串,其长度为 startIndex,包含 S 中索引从 0 到 startIndex - 1 的代码单元,按升序排列。
- 将 C 的 [[RegExpRightContext]] 内部插槽的值设置为一个字符串,其长度为 len - endIndex,包含 S 中索引从 endIndex 到 len - 1 的代码单元,按升序排列。
- 对于每个整数 i 使得 1 ≤ i ≤ 9:
- 如果 i ≤ n,则将 C 的 [[RegExpPareni]] 内部插槽的值设置为 capturedValues 的第 i 个元素。
- 否则,将 C 的 [[RegExpPareni]] 内部插槽的值设置为空字符串。
InvalidateLegacyRegExpStaticProperties ( C)
抽象操作 InvalidateLegacyRegExpStaticProperties 将 %RegExp% 的静态属性的值标记为不可用。
- 断言:C 是拥有 [[RegExpInput]] 内部插槽的对象。
- 将 C 的以下内部插槽的值设置为空:
- [[RegExpInput]]
- [[RegExpLastMatch]]
- [[RegExpLastParen]]
- [[RegExpLeftContext]]
- [[RegExpRightContext]]
- [[RegExpParen1]]
- [[RegExpParen2]]
- [[RegExpParen3]]
- [[RegExpParen4]]
- [[RegExpParen5]]
- [[RegExpParen6]]
- [[RegExpParen7]]
- [[RegExpParen8]]
- [[RegExpParen9]]
RegExp 构造函数的附加属性
以下所有属性都是访问器属性,具有属性 { [[Enumerable]]: false, [[Configurable]]: true }。此外,对于未显式定义 setter 的属性,[[Set]] 属性设置为 undefined。
访问器会检查它们的 this 值,以便这些属性不会被子类继承。
抽象操作
GetLegacyRegExpStaticProperty( C, thisValue, internalSlotName )。
抽象操作 GetLegacyRegExpStaticProperty 用于从遗留 RegExp 静态属性中检索值。
- 断言 C 是一个拥有名为 internalSlotName 的内部插槽的对象。
- 如果 SameValue(C, thisValue) 为 false,则抛出 TypeError 异常。
- 让 val 为 C 的名为 internalSlotName 的内部插槽的值。
- 如果 val 为空,则抛出 TypeError 异常。
- 返回 val。
SetLegacyRegExpStaticProperty( C, thisValue, internalSlotName, val )。
抽象操作 SetLegacyRegExpStaticProperty 用于向遗留 RegExp 静态属性赋值。
- 断言 C 是一个拥有名为 internalSlotName 的内部插槽的对象。
- 如果 SameValue(C, thisValue) 为 false,则抛出 TypeError 异常。
- 让 strVal 为 ? ToString(val)。
- 将 C 的名为 internalSlotName 的内部插槽的值设置为 strVal。
RegExp.input
get RegExp.input
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpInput]])。
set RegExp.input = val
- 执行 ? SetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpInput]], val)。
RegExp.$_
get RegExp.$_
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpInput]])。
set RegExp.$_ = val
- 执行 ? SetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpInput]], val)。
get RegExp.lastMatch
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpLastMatch]])。
get RegExp.$&
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpLastMatch]])。
get RegExp.lastParen
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpLastParen]])。
get RegExp.$+
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpLastParen]])。
get RegExp.leftContext
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpLeftContext]])。
get RegExp.$`
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpLeftContext]])。
get RegExp.rightContext
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpRightContext]])。
get RegExp.$'
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpRightContext]])。
get RegExp.$1
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpParen1]])。
get RegExp.$2
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpParen2]])。
get RegExp.$3
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpParen3]])。
get RegExp.$4
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpParen4]])。
get RegExp.$5
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpParen5]])。
get RegExp.$6
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpParen6]])。
get RegExp.$7
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpParen7]])。
get RegExp.$8
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpParen8]])。
get RegExp.$9
- 返回 ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpParen9]])。
RegExp.prototype.compile ( pattern, flags )
下面的修改将禁用 RegExp.prototype.compile 对于不是 RegExp 直接实例的对象,以及在 realm 不匹配的情况下。
- 让 O 为 this 值。
- 如果 Type(O) 不是 Object 或 Type(O) 是 Object 且 O 没有 [[RegExpMatcher]] 内部插槽,则
- 抛出 TypeError 异常。
- 让 thisRealm 为当前 Realm 记录。
- 让 oRealm 为 O 的 [[Realm]] 内部插槽的值。
- 如果 SameValue(thisRealm, oRealm) 为 false,则抛出 TypeError 异常。
- 如果 R 的 [[LegacyFeaturesEnabled]] 内部插槽的值为 false,则抛出 TypeError 异常。
- 如果 Type(pattern) 是 Object 且 pattern 有 [[RegExpMatcher]] 内部插槽,则
- 如果 flags 不是 undefined,则抛出 TypeError 异常。
- 让 P 为 pattern 的 [[OriginalSource]] 内部插槽的值。
- 让 F 为 pattern 的 [[OriginalFlags]] 内部插槽的值。
- 否则,
- 让 P 为 pattern。
- 让 F 为 flags。
- 返回 ? RegExpInitialize(O, P, F)。