ES6学习笔记(十九)正则的扩展

本文深入探讨ES6中正则表达式的新特性,包括构造函数改进、修饰符扩展如u和y修饰符、Unicode属性类、具名组匹配及后行断言等,解析如何提升正则表达式的灵活性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RegExp构造函数


RegExp构造函数并非ES6中新增的,而是在ES5中就有的,首先来看ES5的用法。

在ES5中,RegExp构造函数有两种用法,可以传入一个参数,也可以传入两个参数

传入一个参数时,需要传入一个正则表达式,此时会返回该正则表达式的拷贝。

var regex = new RegExp(/xyz/i);
// 等价于
var regex = /xyz/i;

传入两个参数时,第一个参数为字符串,第二个参数为正则表达式的修饰符。

var regex = new RegExp('xyz', 'i');
// 等价于
var regex = /xyz/i;

在ES5中,传入两个参数时,如果第一个参数为正则表达式的话,那么会报错。

var regex = new RegExp(/xyz/, 'i');
// Uncaught TypeError: Cannot supply flags when constructing one RegExp from ano

ES6改变了这种行为,当第一个参数为正则表达式时,可以使用第二个参数来修改其修饰符,最后返回的正则表达式的修饰符以第二个参数为准,即使第一个参数里已经写出了修饰符,还是以第二个为准。

new RegExp(/abc/g, 'i').flags
// "i"

字符串的正则方法


在ES6之前,字符串中已经有了match(),replace(),search(),split()四个与正则有关的方法,而在ES6中,将这四个方法定义在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修饰符

u修饰符实际上就是“Unicode 模式”,u修饰符的出现,解决了在使用正则匹配时,超过\uFFFF的字符不能被正确匹配的问题,u修饰符的出现会解决下列匹配过程中出现的问题

1.点字符

在正则中,点字符表示单个除了换行和回车的任意字符,而对于超过\uFFFF的字符来说,会被认为是两个字符,所以下面没加u修饰符的结果为false

/^.$/u.test('\uD83D\uDC2A');
// true
/^.$/.test('\uD83D\uDC2A');
// false

2.Unicode字符表示法

在ES6中新增了使用{}表示Unicode字符,而如果不使用u修饰符的话,会被识别为量词

/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
// a为'\u{61}'

如果要对一个Unicode字符使用量词,不加u修饰符甚至会报错

/\u{61}{2}/.test('aa');
// Uncaught SyntaxError: Invalid regular expression: /\u{61}{2}/: Nothing to repeat
/\u{61}{2}/u.test('aa')
// true

3.预定义模式

u修饰符使预定义模式正确识别Unicode字符

/^\S$/.test('?') // false
/^\S$/u.test('?') // true

/^\S$/匹配一个非空白字符,这里如果不能正确识别Unicode字符,就会返回false。

4.i修饰符

有些Unicode编码不同,但是字型相近,不加u修饰符不能识别非规范的字符

// 下面两个编码结果都为大写的K
'\u004B'
// "K"
'\u212A'
// "K"

/[a-z]/i.test('\u004B')
// true

/[a-z]/i.test('\u212A')
// false

/[a-z]/iu.test('\u212A')
// true

y修饰符

y修饰符被称为“粘连”(sticky)修饰符,其与g修饰符相似,都是全局匹配,但不同的是,g修饰符只要剩余位置有匹配上的就行,而y修饰符要求需要从上一次匹配位置的末尾的下一个位置进行匹配。

// exec方法返回被匹配的内容

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

上面代码中,在第一次匹配后,剩余子串都为"_aa_a",而g修饰符由于只要求剩余位置有的匹配就可以返回内容,所以返回了"aa",而y修饰符要求“粘连”,即要从_开始匹配,所以没有匹配上,返回了null。

单独使用y修饰符时使用match方法无法匹配所有内容,必须结合g修饰符才行。

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

y修饰符遵守lastIndex属性,从其指定的位置开始匹配

var r=/a/y;

r.lastIndex=0; // 从第0个位置开始匹配
r.test('abab');
// true

r.lastIndex=1; // 从第2个位置开始匹配
r.test('abab');
// false

s修饰符

s修饰符是为了解决.匹配单个字符时的特殊情况,.可以匹配任意单个字符,但四个字节的 UTF-16 字符(可以使用u修饰符解决)和行终止符无法匹配。一下四个为行终止符

U+000A 换行符(\n

U+000D 回车符(\r

U+2028 行分隔符(line separator)

U+2029 段分隔符(paragraph separator)

/a.b/.test('a\nb');
// false
/a.b/s.test('a\nb');
// true

属性的扩展


RegExp.prototype.unicode 

该属性值为布尔值,表示是否设置了u修饰符

/\u212A/.unicode
// false
/\u212A/u.unicode
// true

RegExp.prototype.sticky

该属性值为布尔值,表示是否设置了y修饰符

/a/y.sticky
// true
/a/.sticky
// false

RegExp.prototype.flags

/a/g.flags
// "g"
/a/gy.flags
// "gy"
/a/ugy.flags
// "guy"

后行断言


在之前的JavaScript中,只有先行断言,而在ES2018中引入了后行断言

先行断言

/x(?=y)/  匹配在y前面的x

/x(?!y)/  匹配x不在y前面的x

/\d(?=%)/.exec('1% 2') // 匹配后面有%的数字
// ["1"]
/\d(?!%)/.exec('1% 2') // 匹配后面没%的数字
// ["2"]

后行断言

/(?<=y)x/ 匹配在y后面的x

/(?<!y)x/ 匹配不在y后面的x

/(?<=\$)\d/.exec('$1 2')
// ["1"]
/(?<!\$)\d/.exec('$1 2')
// ["2"]

后行断言带来的问题

后行断言会按先右后左的匹配顺序,会导致一些不同的结果

/^(\d+)(\d+)$/.exec('1053') // ["1053", "105", "3"]
/(?<=(\d+)(\d+))$/.exec('1053') // ["", "1", "053"]

上面代码中,第一行的代码没有使用后行断言,由于在贪婪模式下(会尽可能地多匹配),所以第一个括号内匹配的数字个数为3个,第二个括号内匹配的数字个数为1个,所以是"105","3"。而第二段代码使用了后行断言,从右边匹配起,也同样在贪婪模式下,右边的括号内会匹配3个数字,左边的括号匹配1个数字,所以是"1","053"。

Unicode属性类


ES2018引入了匹配符合Unicode某种属性的写法,\p{...}和\P{...}

\p{...}是匹配符合某种属性的内容,而\P{...}是匹配不符合某种属性的内容

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

\p{Script=Greek}指定匹配一个希腊文字母,所以上面的'π'匹配成功

因为是用于匹配Unicode的某种属性,自然必须是对Unicode字符进行匹配,所以使用时必须加上u修饰符,否则会报错。

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

\p{UnicodePropertyName=UnicodePropertyValue}
\p{UnicodePropertyName}
\p{UnicodePropertyValue}

具名组匹配


在之前的JavaScript中,可以使用()分组进行匹配,而ES2018引入了具名组匹配后,可以通过id来分组

简介

具名组匹配通过在括号内以?<id>开头来进行分组,id即后面调用要用到的分组名,这些分组在groups属性上调用

const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31

如果没有对应得分组名,就会返回undefined

matchObj.groups.as // undefined

replace替换

在使用replace替换时,使用$<分组名>调用对应内容

let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;

'2015-01-02'.replace(re, '$<day>/$<month>/$<year>')
// '02/01/2015'

\k引用

使用\k<分组名>可引用正则表达式中具名组的值

const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
RE_TWICE.test('abc!abc') // true
RE_TWICE.test('abc!ab') // false

 



参考自阮一峰的《ECMAScript6入门》


ES6学习笔记目录(持续更新中)

ES6学习笔记(一)let和const

ES6学习笔记(二)参数的默认值和rest参数

ES6学习笔记(三)箭头函数简化数组函数的使用

ES6学习笔记(四)解构赋值

ES6学习笔记(五)Set结构和Map结构

ES6学习笔记(六)Iterator接口

ES6学习笔记(七)数组的扩展

ES6学习笔记(八)字符串的扩展

ES6学习笔记(九)数值的扩展

ES6学习笔记(十)对象的扩展

ES6学习笔记(十一)Symbol

ES6学习笔记(十二)Proxy代理

ES6学习笔记(十三)Reflect对象

ES6学习笔记(十四)Promise对象

ES6学习笔记(十五)Generator函数

ES6学习笔记(十六)asnyc函数

ES6学习笔记(十七)类class

ES6学习笔记(十八)尾调用优化

ES6学习笔记(十九)正则的扩展

ES6学习笔记(二十)ES Module的语法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值