本文详解ES6对正则表达式的扩展,包括RegExp构造函数支持第二个参数指定修饰符、字符串方法调用RegExp实例方法、u修饰符处理Unicode码点、y粘连修饰符的工作原理及sticky属性,以及新增的flags属性和RegExp.escape()提议。
本文通过具体实例介绍ES6中正则表达式的扩展功能,供读者参考学习。
RegExp构造函数
1. RegExp构造函数
在ES5中,RegExp构造函数的参数使用方式如下:
当参数为字符串时,第二个参数用于指定正则表达式的修饰符(flag)。
若参数为正则表达式对象,ES5会返回该正则对象的副本。但ES5不允许在此时使用第二个参数添加修饰符,否则将抛出错误。
ES6对此行为进行了修改。如果RegExp构造函数的第一个参数是正则对象,可以通过第二个参数指定新的修饰符。此时,返回的正则表达式将忽略原对象的修饰符,仅使用新指定的修饰符。
字符串的正则表达式
2. 字符串的正则表达式
字符串对象提供了四个可使用正则表达式的方法:match()、replace()、search()和split()。
ES6在语言内部将这四个方法全部改为调用RegExp的实例方法,确保所有与正则相关的方法均定义在RegExp对象上。
String.prototype.match 调用 RegExp.prototype[Symbol.match]
String.prototype.replace 调用 RegExp.prototype[Symbol.replace]
String.prototype.search 调用 RegExp.prototype[Symbol.search]
String.prototype.split 调用 RegExp.prototype[Symbol.split]
u修饰符
3. u修饰符
ES6为正则表达式引入了u修饰符,表示“Unicode模式”,用于正确处理码点大于\uFFFF的Unicode字符。
这意味着正则表达式能够正确处理四个字节的UTF-16编码。对于码点大于0xFFFF的Unicode字符,点字符(.)无法识别,必须添加u修饰符。
ES6新增使用大括号表示Unicode字符的语法,在正则表达式中必须加上u修饰符才能识别这种表示法。
/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
/\u{20BB7}/u.test('𠮷') // true
①.点字符
在正则表达式中,点(.)字符匹配除换行符外的任意单个字符。对于码点大于0xFFFF的Unicode字符,点字符无法识别,必须添加u修饰符。
var s = '';
/^.$/.test(s) // false
/^.$/u.test(s) // true
上述代码表明,若不添加u修饰符,正则表达式会将字符串视为两个字符,导致匹配失败。
②. 量词
使用u修饰符后,所有量词都能正确识别码点大于0xFFFF的Unicode字符。
/a{2}/.test('aa') // true
/a{2}/u.test('aa') // true
/ {2}/.test(' ') // false
/ {2}/u.test(' ') // true
此外,只有在u修饰符存在的情况下,Unicode表达式中的大括号才会被正确解读;否则,大括号会被解读为量词。
/^\u{3}$/.test('uuu') // true
在上面的代码中,由于正则表达式未使用u修饰符,大括号被解读为量词。添加u修饰符后,大括号被解读为Unicode表达式。
③. 预定义模式
u修饰符还影响预定义模式能否正确识别码点大于0xFFFF的Unicode字符。
\S是预定义模式,匹配所有非空格字符。只有添加u修饰符,它才能正确匹配码点大于0xFFFF的Unicode字符。
利用这一特性,可以编写一个正确返回字符串长度的函数。
function codePointLength(text) {
var result = text.match(/[\s\S]/gu);
return result ? result.length : 0;
}
var s = '𠮷𠮷';
s.length // 4
codePointLength(s) // 2
④. i修饰符
某些Unicode字符编码不同但字形相似,例如\u004B与\u212A均表示大写K。
/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true
在上面的代码中,若不添加u修饰符,将无法识别非规范的大写K字符。
y修饰符
4. 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修饰符从该位置向后搜索,直到找到匹配为止。
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修饰符同样遵守lastIndex属性,但要求必须在lastIndex指定的位置发现匹配。
进一步而言,y修饰符隐含了头部匹配的标志^。
/b/y.exec('aba')
// null
单独使用y修饰符时,match方法仅返回第一个匹配结果;必须与g修饰符联用,才能返回所有匹配结果。
sticky属性与flags属性
5. sticky属性
表示是否设置了y修饰符。
var r = /hello\d/y;
r.sticky // true
6. flags属性
ES6为正则表达式新增了flags属性,用于返回正则表达式的修饰符(这是前面过程的逆向操作)。
RegExp.escape()方法
7. RegExp.escape()
字符串必须转义才能作为正则模式。已有提议将此需求标准化,作为RegExp对象的静态方法RegExp.escape()。
function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
}
let str = '/path/to/resource.html?search=query';
escapeRegExp(str)
// "\/path\/to\/resource\.html\?search=query"
PS:这里再为大家提供2款非常方便的正则表达式工具供大家参考使用:
JavaScript正则表达式在线测试工具: http://tools.jb51.net/regex/javascript
正则表达式在线生成工具: http://tools.jb51.net/regex/create_reg
更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《ECMAScript6(ES6)入门教程》、《JavaScript正则表达式技巧大全》、《JavaScript字符与字符串操作技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript错误与调试技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。