好久没有写文章了,很惭愧。
昨天同事遇到了这样一个问题,用正则进行循环判断,简化的代码大概是下面这样:
var
这里是不是有人会觉得奇怪,为什么第二个是 false 呢。
test()
其实如果我们仔细看过MDN上关于test的描述,应该就知道了:
关于这个lastIndex,很多小伙伴可能没有听过也没有用过,让我们看看下面的代码:
var
可以看到,reg 含有一个不可枚举的 lastIndex 属性。当然,如果你直接用 Object.keys(reg) 是看不到的:
var
(知识点:getOwnPropertyDescriptors() 和 keys() 的区别是什么呢。keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组。)
这个就是造成上面第二个是false的原因。因为每执行一次,其实我们的lastIndex都会变化:
var
根据上面的代码可以看到,这个lastIndex是会循环的。
所以第二次就出了问题。如果我们想让一开始的代码正常工作,该怎么办呢?
var
我们需要在每次执行后手动设置lastIndex。
test() 应用场景
我们举一个应用场景:后端传回来了一个图片 url 的数组,有 jpg、png 各种格式的,我们需要拿到以 jpg 结尾的图片:
首先,我们看看下面的代码:
const
(关于判断图片格式的这个正则大家不要纠结,其实是不完善的,比如url的名字是 ajpg.jpg 这种也可以匹配到,这个不重要,不是本文重点)
乍一看,觉得没毛病。我们看看结果:
WTF???
你肯定反应过来了,因为lastIndex变了,所以和预期不一致了,我们输出一下看看:
可以看到,true和false是依次输出的,所以我们没有拿到全部的jpg格式的url。
还有,就是如果当前匹配失败了,lastIndex也会重置为0,这一点也很重要,不然后面的就匹配不到了。
所以解决方法之一就是手动重置lastIndex:
其实,最好的方法就是直接去掉g,因为这种场景其实是不需要加上g的。这样lastIndex就一直是0,不会变了。结果也是符合预期的:
exec
此外,lastIndex 还和另一个正则方法 exec() 也是好兄弟,关系很近。用法和 test() 原理一样。
很多人分不清 exec 和 match 的关系,或者可能没有用过 exec。
`match` 和 `exec`的关系:
- 首先,两个方法的来源是不一样的:
String
- 然后,不加g的时候是一样的:
- 然后,加了g的话:
match就没有捕获组了:
这个时候我们就需要用exec()方法了。
捕获组
关于捕获组,这里简单介绍一下。
捕获组分为四类:
- 捕获 ()
- 不捕获 (?:)
- 正向捕获 (?=)
- 反向捕获 (?!)
捕获组的功能很强大,这里就举两个例子:
例1:对日期进行任意格式的转换
var
例2: 千分位数字
'12345678'
因此,exec结合捕获组就可以实现很多复杂的逻辑。
exec() 应用场景
举个例子,我们要把一个字符串` a1b2c3d4e5`里面的偶数的数字变成0。(方法肯定很多,这里只考虑用正则的方式)
肯定有同学说直接用 `replace` 就行了:
'a1b2c3d4e5'
(因为这里的转换条件比较简单,如果是很复杂的转换,比如我要把偶数的数字先转成ASCII码,然后反转呢?主要就是意思一下哈。)
我个人觉得exec比match强大的地方在于它可以深入到内部,暂停执行,类似 generate 函数,而不像match一样是个黑盒。
exec() 和 lastIndex
关于这个,其实业务中感觉用的不多,就举个例子吧:
var
代码大家肯定看得懂,我就不说了。`match` 是无法实现这样的功能的,因为匹配完就丢弃了。
使用exec() 要注意的点就是不要写出死循环(毕竟有while)。
(完)