[关闭]
@xiaoyixy 2018-11-08T09:05:53.000000Z 字数 4058 阅读 964

ES6 正则表达式扩展

Note ES6 ECMAScript6


扩展

new RegExp(pattern [, flags])

第一个参数为正则表达式而第二个标志参数存在时,new RegExp(/ab+c/, 'i') 不再抛出 TypeError

  1. /ab+c/i;
  2. new RegExp('ab+c', 'i');
  3. new RegExp(/ab+c/, 'i');
  4. new RegExp(/ab+c/, 'i');
  5. // Starting with ECMAScript 6, new RegExp(/ab+c/, 'i') no longer throws a TypeError ("can't supply flags when constructing one RegExp from another") when the first argument is a RegExp and the second flags argument is present. A new RegExp from the arguments is created instead.

修饰符 u

Unicode; 将模式视为 Unicode 序列点的序列,用来正确处理大于 \uFFFF 的 Unicode 字符

  1. // \uD83D\uDC2A是一个四个字节的 UTF-16 编码,代表一个字符
  2. // ES5 不支持四个字节的 UTF-16 编码,会将其识别为两个字符
  3. /^\uD83D/u.test('\uD83D\uDC2A') // false
  4. /^\uD83D/.test('\uD83D\uDC2A') // true
  5. // 1. 对于码点大于0xFFFF的 Unicode 字符,点字符(.)不能识别,必须加上 u 修饰符。
  6. var s = '𠮷';
  7. /^.$/.test(s) // false
  8. /^.$/u.test(s) // true
  9. // 2. ES6 新增了使用大括号表示 Unicode 字符,这种表示法在正则表达式中必须加上u修饰符,才能识别当中的大括号,否则会被解读为量词。
  10. /\u{61}/.test('a') // false
  11. /\u{61}/u.test('a') // true
  12. /\u{20BB7}/u.test('𠮷') // true
  13. // 3. 使用u修饰符后,所有量词都会正确识别码点大于0xFFFF的 Unicode 字符。
  14. /a{2}/.test('aa') // true
  15. /a{2}/u.test('aa') // true
  16. /𠮷{2}/.test('𠮷𠮷') // false
  17. /𠮷{2}/u.test('𠮷𠮷') // true
  18. // 4. 预定义模式
  19. // 下面代码的\S是预定义模式,匹配所有非空白字符。只有加了u修饰符,它才能正确匹配码点大于0xFFFF的 Unicode 字符。
  20. /^\S$/.test('𠮷') // false
  21. /^\S$/u.test('𠮷') // true
  22. // 5. i 修饰符
  23. // 有些 Unicode 字符的编码不同,但是字型很相近,比如,\u004B与\u212A都是大写的K
  24. /[a-z]/i.test('\u212A') // false
  25. /[a-z]/iu.test('\u212A') // true

y 修饰符

粘性匹配; 仅匹配目标字符串中此正则表达式的 lastIndex 属性指示的索引(并且不尝试从任何后续的索引匹配)。

  1. // g
  2. var regex1 = RegExp('foo*','g');
  3. var regex2 = RegExp('foo*','y');
  4. var str1 = 'football, foosball';
  5. var array1, array2;
  6. while ((array1 = regex1.exec(str1)) !== null) {
  7. console.log(`Found ${array1[0]}. Next starts at ${regex1.lastIndex}.`);
  8. // "Found foo. Next starts at 3."
  9. // "Found foo. Next starts at 13."
  10. }
  11. // y
  12. while ((array2 = regex2.exec(str1)) !== null) {
  13. console.log(`Found ${array2[0]}. Next starts at ${regex2.lastIndex}.`);
  14. // "Found foo. Next starts at 3."
  15. }

单单一个y修饰符对match方法,只能返回第一个匹配,必须与g修饰符联用,才能返回所有匹配。

  1. 'a1a2a3'.match(/a\d/y) // ["a1"]
  2. 'a1a2a3'.match(/a\d/gy) // ["a1", "a2", "a3"]

s 修饰符:dotAll 模式[ES2018]

ES2018 引入s修饰符,使得.可以匹配任意单个字符。

正则表达式中,点(.)是一个特殊字符,代表任意的单个字符,但是有两个例外。一个是四个字节的 UTF-16 字符,这个可以用u修饰符解决;另一个是行终止符(line terminator character)。

  1. /foo.bar/.test('foo\nbar') // false
  2. /foo[^]bar/.test('foo\nbar') // true
  3. // ES2018
  4. /foo.bar/s.test('foo\nbar') // true

后行断言[ES2018]

JavaScript 语言的正则表达式,只支持先行断言(lookahead)和先行否定断言(negative lookahead),不支持后行断言(lookbehind)和后行否定断言(negative lookbehind)。ES2018 引入后行断言,V8 引擎 4.9 版(Chrome 62)已经支持。

  1. /\d+(?=%)/.exec('100% of US presidents have been male') // ["100"]
  2. /\d+(?!%)/.exec('that’s all 44 of them') // ["44"]
  3. /(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill')
  4. // ["100"]
  5. /(?<!\$)\d+/.exec('it’s is worth about €90')
  6. // ["90"]

“后行断言”的实现,需要先匹配/(?<=y)x/的x,然后再回到左边,匹配y的部分。这种“先右后左”的执行顺序,与所有其他正则操作相反,导致了一些不符合预期的行为。

  1. /(?<=(\d+)(\d+))$/.exec('1053') // ["", "1", "053"]
  2. /^(\d+)(\d+)$/.exec('1053') // ["1053", "105", "3"]
  3. /(?<=(o)d\1)r/.exec('hodor') // null
  4. /(?<=\1d(o))r/.exec('hodor') // ["r", "o"]

Unicode 属性类[ES2018]

ES2018 引入了一种新的类的写法\p{...}和\P{...},允许正则表达式匹配符合 Unicode 某种属性的所有字符。

Unicode 属性类要指定属性名和属性值。

  1. \p{UnicodePropertyName=UnicodePropertyValue}

对于某些属性,可以只写属性名,或者只写属性值。

  1. \p{UnicodePropertyName}
  2. \p{UnicodePropertyValue}

\P{…}\p{…} 的反向匹配,即匹配不满足条件的字符。

注意,这两种类只对 Unicode 有效,所以使用的时候一定要加上u修饰符。如果不加u修饰符,正则表达式使用\p和\P会报错,ECMAScript 预留了这两个类。

  1. const regexGreekSymbol = /\p{Script=Greek}/u;
  2. regexGreekSymbol.test('π') // true

具名组匹配[ES2018]

正则表达式使用圆括号进行组匹配

  1. const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;
  2. const matchObj = RE_DATE.exec('1999-12-31');
  3. const year = matchObj[1]; // 1999
  4. const month = matchObj[2]; // 12
  5. const day = matchObj[3]; // 31

ES2018 引入了具名组匹配(Named Capture Groups),允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。

  1. const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
  2. const matchObj = RE_DATE.exec('1999-12-31');
  3. const year = matchObj.groups.year; // 1999
  4. const month = matchObj.groups.month; // 12
  5. const day = matchObj.groups.day; // 31

如果要在正则表达式内部引用某个“具名组匹配”,可以使用\k<组名>的写法。

  1. const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
  2. RE_TWICE.test('abc!abc') // true
  3. RE_TWICE.test('abc!ab') // false
  4. // 数字引用(\1)依然有效。
  5. const RE_TWICE = /^(?<word>[a-z]+)!\1$/;
  6. RE_TWICE.test('abc!abc') // true
  7. RE_TWICE.test('abc!ab') // false

参考

RegExp
正则的扩展

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注