ES2024 对正则表达式添加了 v 修饰符,含义为 “Unicode 集合”,用来方便处理 Unicode 集合。v 修饰符是 u 修饰符的升级模式,使用了 v 修饰符就意味着包含了 u 修饰符的功能。比如 /\p{Emoji}/u
和 /\p{Emoji}/v
是等效的。
unicodeSets 的能力
unicodeSets 有以下 3 个功能:
\p
用来引用预定义的 Unicode 字符属性集合
比如 \p{RGI_Emoji}
匹配任何 RGI(Recommended for General Interchange)emoji 表情符号。
字符串集合三种集合操作,差异、相交和联合
/^[\w--[a-g]]$/v.test('a') // false
/^[\w--[a-g]]$/v.test('i') // true
/[\p{ASCII}&&\p{Letter}]/v; // ASCII letters
/[[\p{ASCII}&&\p{Letter}]\p{Number}]/v; // ASCII letters, or any digit
集合中的多节点字符串,使用一个新的 \q
转义
/[\r\n\q{\r\n|NEWLINE}]/v; // Matches \r, \n, \r\n or NEWLINE
unicodeSets 的兼容问题
unicodeSets 可以被 babel 转译以支持低版本浏览器
比如 /[\p{ASCII}&&\p{Letter}]/v
经过 babel 转义后生成了 /[A-Za-z]/
正则对象还增加了 unicodeSets 属性,可以获取到是否有 v 修饰符
var a = /[\p{ASCII}&&\p{Letter}]/v;
console.log(a.flags); // "v"
console.log(a.unicodeSets); // true
然而,如果使用 babel,转译后的代码将无法正确获取 flags 和 unicodeSets 了。
比如上述代码经过 babel 转义后变成了
var a = /[A-Za-z]/;
console.log(a.flags); // ""
console.log(a.unicodeSets); // false
与浏览器直接运行不同。
unicodeSets 的出现还有可能引起旧有代码出现错误
if (xxx.unicode) {
// do somthing
}
比如上述代码,判断正则表达式如果支持 unicode,则处理一些逻辑。而在有了 unicodeSets,这段代码就没有处理 unicodeSets 的情况。因为只有 v 标志时,unicode 为 false。
unicodeSets 兼容方案
解决 unicodeSets 有 2 个方案,一是通过 polyfill 处理,二是通过 babel 处理。
假如使用 polyfill 处理,需要先将正则表达式字面量用 babel 转化为 new 表达式。然后再引入 polyfill。
这么做的好处是能做的和原生正则表达式一样,但是代价是:
- 需要大量修改原生功能,比如 String 的 replace、match 方法都需要 polyfill。这不符合尽量使用原生功能的原则。
- 正则表达式的 polyfill 要实现 \p 必然含有大量的字典数据,导致体积巨大。这不符合尽量不使用字典的原则。
- 根本找不到正则表达式的 polyfill 实现来用
因此建议通过 babel 处理正则表达式,并有限制的使用其功能。
在管理上
- 禁止使用正则表达式的 unicodeSets 属性
- 禁止使用正则表达式的 flags 属性
- 通过 new 创建正则表达式,第二个参数不得含有 v 标志
看似在开发时稍加注意就行,其实不然。对大多数一线开发来说,要实现一个功能的步骤就是:百度、Ctrl+C、Ctrl+V,根本不会去考虑有什么问题,然后有兼容性的代码就被复制到项目中去了。像兼容性这样的问题本来就应该是架构层考虑的。因此我们在做前端基建时,除了要为开发人员配置 babel 外,还需使用 eslint 来限制开发人员的写法。
使用 eslint 插件 eslint-plugin-ts-compat
可以校验一些兼容性问题,用其中的 ts-compat/no-regexp-unicode-sets
规则处理 unicodeSets 问题。
这个插件是基于 typescript,只有在使用 typescript 的前提下才能使用。只有用了 typescript,才能知道类型,才能根据类型禁用属性。这也是我向来建议业务代码也要使用 typescript 的原因。有了 typescript,哪些能用,哪些不能用都一目了然;没有 typescript,能点出哪些东西都不一定。