环视概念简介
环视(look around)只进行子表达式的匹配,并不占字符,匹配到的内容不进行保存,环视最终的匹配结果就是一个位置,子表达式为零宽度,因此也叫零宽断言(zero-width assertion) 。
环视按照方向可分为顺序环视(lookahead)和逆序环视(lookbehind)两种,按匹配条件分肯定(positive)和否定(negative)两种,组合起来就是四种模式。
| 四种组合模式 | 肯定环视(匹配符合条件的内容) | 否定环视(匹配不符合条件的内容) |
|---|---|---|
| 顺序环视(从左往右环视) | 顺序肯定环视 ?= | 顺序否定环视 ?! |
| 逆序环视(从右往左环视) | 逆序肯定环视 ?<= | 逆序否定环视 ?<! |
💡记忆方法:问号表示询问,作为开头符号表示询问接下来的条件是否符合预期 (不同于问号在正则放在规则尾部时表达询问前面的内容是否存在)
最直观的就是顺序肯定环视,问号后面紧跟等号,表示询问接下来的内容是否等于指定规则,与之对应的则是使用感叹号表达否定,询问接下来的内容是否不等于指定的规则。
而逆序环视相对于顺序环视的区别就是增加一个小于号,小于号箭头向左,以此来表达“逆序”(因为正则默认的匹配顺序是从左往右),后面的等号或感叹号则与正序环视相同。
JS环视语法兼容性
Safari 对于逆序环视的兼容性较差,仅支持 >=16.4 的版本
正序环视兼容性则好一些,基本覆盖大部分用户使用的浏览器版本,所以一般不需要考虑兼容性问题。
相关材料
- 原始提案:github.com/tc39/propos…
- JS 语言实现规范:tc39.es/ecma262/#se…
- Chromium 的实现及测试用例:codereview.chromium.org/1398033002/…
- MDN 介绍文档:developer.mozilla.org/zh-CN/docs/…
兼容性方案
逆序环视是很好的正则语法糖,可以简化很多规则匹配的写法。但由于 Safari 目前对它的兼容性还不太理想,因此如果希望避免因误用该语法而导致运行时报错,需要考虑使用工程化手段在项目编译阶段检测出该语法的误用。
比如可以使用 eslint 插件 eslint-plugin-es 来禁用逆序环视的使用(该插件虽然非 eslint 官方插件,但仓库作者为 eslint 核心维护者)
优点
避免源码中误引入逆序环视,导致 safari/iOS 16.4 以下设备无法使用(比如浏览器白屏,报错如下图)
缺点
无法检测到 node_modules 内三方包的逆序环视使用(因为一般使用 eslint 只会对项目源码进行检查)
语法替代方案示例
| 场景需求描述 | 使用逆序环视 | 不使逆序环视 |
|---|---|---|
| 匹配由字母、数字、下划线组成的字符串,下划线不能出现在开始或结束位置 | ^(?!_)[a-zA-Z0-9_]+(?<!_)$ | ^[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?$ |
逆序环视是很好用的匹配语法,但本质上是编程语言提供的正则语法糖,如果需要兼容暂未支持该语法的使用环境,也可以使用其他方式实现相同的效果,只是没有使用逆序环视那么方便而已。
由于正则是用于匹配规则的语法,因此并没有所谓的通用替代方案,需要根据具体的场景来考虑不依赖逆序环视的匹配方式