先说遇到的问题,
const numberRegexp = /\d+/g;
const isNumber = (v) => numberRegexp.test(v);
isNumber('22'); // true
isNumber('22'); // false
惊呆了,不是同一个正则吗?怎么可能啊?
全局匹配(g)
上面的问题就是使用了全局匹配,带来的影响.如果正则表达式设置了全局标志,test()
的执行会改变正则表达式lastIndex
属性。连续的执行test()
方法,后续的执行将会从 lastIndex
处开始匹配字符串,(exec()
同样改变正则本身的 lastIndex
属性值).
lastIndex
是用来指定下一次匹配的起始索引
const numberRegexp = /\d+/g;
const isNumber = (v) => numberRegexp.test(v);
isNumber('22'); // true
// but numberRegexp.lastIndex 2
isNumber('22'); // false
// 所以后面的匹配从2开始,后面自然就是找不到匹配了就是false
正则的lastIndex
只有正则表达式使用了表示全局检索的 “g” 标志时,该属性才会起作用。此时应用下面的规则:
- 如果 lastIndex 大于字符串的长度,则 regexp.test 和 regexp.exec 将会匹配失败,然后 lastIndex 被设置为 0。
- 如果 lastIndex 等于字符串的长度,且该正则表达式匹配空字符串,则该正则表达式匹配从 lastIndex 开始的字符串。
- 如果 lastIndex 等于字符串的长度,且该正则表达式不匹配空字符串 ,则该正则表达式不匹配字符串,lastIndex 被设置为 0.。
- 否则,lastIndex 被设置为紧随最近一次成功匹配的下一个位置。
正则相关的修饰符
修饰符i
修饰符i 就是忽略大小写的修饰符
修饰符m
修饰符m 修饰符 是多行修饰符; 将开始和结束字符(^和$)视为在多行上工作(也就是,分别匹配每一行的开始和结束(由 \n 或 \r 分割),而不只是只匹配整个输入字符串的最开始和最末尾处。
修饰符u
修饰符u 修饰符,将模式视为Unicode序列点的序列, 是对UTF-16编码的支持,ES5的时候,还不支持UTF-16, 对于码点大于0xFFFF的Unicode字符,不能识别,必须加上u修饰符。
const a = "𠮷";
/^.$/.test(a); // false
/^.$/u.test(a); // true
/[a-z]/i.test('\u212A') // false \u212A是非规范的K字符
/[a-z]/iu.test('\u212A') // true
修饰符y
修饰符y 与g修饰符类似,也是全局匹配,后一次匹配都是从上一次匹配成功的下一个位置开始。区别在于,g修饰符只要剩余位置中存在匹配即可,而y修饰符是必须从剩余第一个开始。
const s = 'aaa_aa_a';
const r1 = /a+/g;
const r2 = /a+/y;
r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]
r1.exec(s) // ["aa"] 剩余 '_aa_a' r1 只要剩下的匹配到就好了
r2.exec(s) // null // 必须冲第一个开始,第一个是_,所以匹配不上