y 修饰符
除了 u 修饰符,ES6 还为正则表达式添加了 y 修饰符,叫做 "粘连"(sticky)修饰符.
y 修饰符的作用与 g 修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始.不同之处在于,g 修饰符只要剩余位置中存在匹配就可,而 y 修饰符确保匹配必须从剩余的第一个位置开始,这也就是 "粘连" 的涵义.
var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;
r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]
r1.exec(s) // ["aa"]
r2.exec(s) // null
上面代码有两个正则表达式,一个使用 g 修饰符,另一个使用 y 修饰符.这两个正则表达式各执行了两次,第一次执行的时候,两者行为相同,剩余字符串都是_aa_a.由于 g 修饰没有位置要求,所以第二次执行会返回结果,而 y 修饰符要求匹配必须从头部开始,所以返回 null.
如果改一下正则表达式,保证每次都能头部匹配,y 修饰符就会返回结果了.
var s = 'aaa_aa_a';
var r = /a+_/y;
r.exec(s) // ["aaa_"]
r.exec(s) // ["aa_"]
上面代码每次匹配,都是从剩余字符串的头部开始.
使用 lastIndex 属性,可以更好地说明 y 修饰符.
const REGEX = /a/g;
// 指定从 2 号位置(y)开始匹配
REGEX.lastIndex = 2;
// 匹配成功
const match = REGEX.exec('xaya');
// 在 3 号位置匹配成功
match.index // 3
// 下一次匹配从 4 号位开始
REGEX.lastIndex // 4
// 4 号位开始匹配失败
REGEX.exec('xaxa') // null
上面代码中,lastIndex 属性指定每次搜索的开始位置,g 修饰符从这个位置开始向后搜索,直到发现匹配为止.
y 修饰符同样遵守 lastIndex 属性,但是要求必须在 lastIndex 指定的位置发现匹配.
const REGEX = /a/y;
// 指定从 2 号位置开始匹配
REGEX.lastIndex = 2;
// 不是粘连,匹配失败
REGEX.exec('xaya') // null
// 指定从 3 号位置开始匹配
REGEX.lastIndex = 3;
// 3 号位置是粘连,匹配成功
const match = REGEX.exec('xaxa');
match.index // 3
REGEX.lastIndex // 4
实际上,y 修饰符号隐含了头部匹配的标志 ^.
/b/y.exec('aba')
// null
上面代码由于不能保证头部匹配,所以返回 null.y 修饰符的设计本意,就是让头部匹配的标志 ^ 在全局匹配中都有效.
在 split 方法中使用 y 修饰符,原字符串必须以分隔符开头.这也意味着,只要匹配成功,数组的第一个成员肯定是空字符串.
// 没有找到匹配
'x##'.split(/#/y)
// ['x##']
// 找到两个匹配
'##x'.split(/#/y)
// ['', '','x']
后续的分隔符只有紧跟前面的分隔符,才会被识别.
'#x#'.split(/#/y)
// ['', 'x#']
'##'.split(/#/y)
// ['', '','']
下面是字符串对象的 replace 方法的例子.
const REGEX = /a/gy;
'aaxa'.replace(REGEX,'-') //'--xa'
上面代码中,最后一个 a 因为不是出现在下一次匹配的头部,所以不会被替换.
单单一个 y 修饰符对 match 方法,只能返回第一个匹配,必须与 g 修饰符联用,才能返回所有匹配.
'a1a2a3'.match(/a\d/y) // ["a1"]
'a1a2a3'.match(/a\d/gy) // ["a1", "a2", "a3"]
y 修饰符的一个应用,是从字符串提取 token(词元),y 修饰符确保了匹配之间不会有漏掉的字符.
const TOKEN_Y = /\s*(\+|[0-9]+)\s*/y;
const TOKEN_G = /\s*(\+|[0-9]+)\s*/g;
tokenize(TOKEN_Y,'3 + 4')
// ['3', '+', '4']
tokenize(TOKEN_G,'3 + 4')
// ['3', '+', '4']
function tokenize(TOKEN_REGEX, str) {
let result = [];
let match;
while (match = TOKEN_REGEX.exec(str)) {
result.push(match[1]);
}
return result;
}
上面代码中,如果字符串里面没有非法字符,y 修饰符与 g 修饰符的提取结果是一样的.但是,一旦出现非法字符,两者的行为就不一样了.
tokenize(TOKEN_Y,'3x + 4')
// ['3']
tokenize(TOKEN_G,'3x + 4')
// ['3', '+', '4']
上面代码中,g 修饰符会忽略非法字符,而 y 修饰符不会,这样就很容易发现错误.
来源: https://www.2cto.com/kf/201801/713593.html