一段代码引起的思考:正则表达式
var r = /\d/g; console.log(r.test('1')); console.log(r.test('1')); console.log(r.test('2')); console.log(r.test('2'));
先看看如上的代码,不要执行,本身先猜想下结果。数组
正则在各个语言中,实现的标准并不彻底一致。咱们这里就讨论在 JavaScript
中的实现。测试
在 JavaScript
中,正则有四个修饰符: global, ignoreCase, multiline, unicode
,详细请参考:MDN RegExp。prototype
它们的含义以下:code
i
i
比较易懂,在匹配的时候会忽略大小写,示例以下:regexp
var text = 'abc'; var r = /abc/; console.log(r.test('abc')); // true console.log(r.test('Abc')); // false r = /abc/i; console.log(r.test('abc')); // true console.log(r.test('Abc')); // true
m
当匹配多行时,默认是全文本匹配,使用 m
以后,将对每一行进行单独匹配。索引
// 定义一个多行文本 var text = `a b c A B C`; var r = /c$/; // 匹配以C结尾的文本 console.log(r.test(text)); // false 全文本匹配,匹配不上 r = /c$/m; console.log(r.test(text)); // true 多行匹配,c是第一行的末尾,匹配成功
u
使用修饰符 u
,将采用 Unicode
模式进行匹配,必需要在正则中包含unicode才能看到效果:ip
var r = /\u{61}/; // 匹配61次u字符 console.log(r.test('a')); // false, 明显匹配不上 r = /\u{61}/u; // Unicode模式,\u{61} = 'a' console.log(r.test('a')); // true, Unicode下能匹配 r = /\u{61}{3}/u; // 匹配3个a,注意正则写法 console.log(r.test('aaa')); // true
注意:在/u模式下,正则中 \u{xxx} 是一个总体,不可拆分。unicode
g
接下来,进入本文的重点,修饰符 g
的匹配模式。字符串
首先,咱们先回到开头的那段代码,我想大部分人的答案多是:true true true true
吧。
实际上,正确答案是:true false true false
,是否是以为难以想象?接着往下看。
RegExp.prototype.test()
方法的逻辑是:当找到第一个匹配项时,返回 true
。查找整个字符串都没有找到匹配项,返回 false
。
那具体又是如何查找的呢?这里咱们就要看 RegExp
的另一个方法了:RegExp.prototype.exec()
。
注意观察这个方法的返回值:
var r = /\d/; // 注意看,返回值是一个数组,除了匹配到的元素以外,还有一个 index 属性 console.log(r.exec('123')); // ["1", index: 0, input: "123"]
关键就是 exec
返回值中的 index
属性,这个属性标识从输入文本的哪个索引处开始查找匹配项。
若是不加 g
,每次都是从索引0处开始查找。
var r = /\d/; console.log(r.exec('123')); // ["1", index: 0, input: "123"] console.log(r.exec('123')); // ["1", index: 0, input: "123"] console.log(r.exec('123')); // ["1", index: 0, input: "123"]
那若是加 g
呢?
var r = /\d/g; console.log(r.exec('123')); // ["1", index: 0, input: "123"] console.log(r.exec('123')); // ["2", index: 1, input: "123"] console.log(r.exec('123')); // ["3", index: 2, input: "123"] console.log(r.exec('123')); // null
从结果咱们能够看出,这个 index
是在变化的,当找不到匹配项时,会返回 null。
总结一下:修饰符 g
的做用,是标识是否须要全局存储这个index。
有了这个理解,那么回到以前的问题,那就说得通了。咱们使用了 g
修饰符,那么会存储上一次的 index
,当执行第二次 console.log(r.test('1'));
时,索引为1,固然就匹配不上了,因此就返回了 false
。
那问题又来了,为何第三次,又返回了 true
呢?
仍是先看代码:
var r = /\d/g; console.log(r.exec('12')); // ["1", index: 0, input: "12"] console.log(r.exec('12')); // ["2", index: 1, input: "12"] console.log(r.exec('12')); // null console.log(r.exec('12')); // ["1", index: 0, input: "12"]
当发现已经匹配不上元素时,会将这个 index
从新设置为 0
。
这也就解释了最开始的整个代码。