-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Jay.M.Hu
committed
May 27, 2017
1 parent
89a61e2
commit f3b39c3
Showing
1 changed file
with
140 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
--- | ||
title: 你真的理解正则修饰符吗 | ||
date: 2017-05-27 10:25:04 | ||
--- | ||
|
||
# 0、前言 | ||
|
||
一段代码引发的思考: | ||
|
||
```js | ||
var r = /\d/g; | ||
console.log(r.test('1')); | ||
console.log(r.test('1')); | ||
console.log(r.test('2')); | ||
console.log(r.test('2')); | ||
``` | ||
|
||
先看看如上的代码,不要执行,自己先猜测下结果。 | ||
|
||
# 1、正则修饰符 | ||
|
||
正则在各个语言中,实现的标准并不完全一致。我们这里就讨论在 `JavaScript` 中的实现。 | ||
|
||
在 `JavaScript` 中,正则有四个修饰符: `global, ignoreCase, multiline, unicode`,详细请参考:[MDN RegExp](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp)。 | ||
|
||
它们的含义如下: | ||
|
||
* global(g) 针对字符串中所有可能的匹配来测试正则表达式 | ||
* ignoreCase(i) 匹配时忽略大小写 | ||
* multiline(m) 多行输入将被视为多行(此时开始^和结尾匹配$可以在每行中进行匹配) | ||
* unicode(u) 对字符串采用unicode进行匹配 | ||
|
||
## 修饰符:`i` | ||
|
||
`i` 比较易懂,在匹配的时候会忽略大小写,示例如下: | ||
|
||
```js | ||
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` 之后,将对每一行进行单独匹配。 | ||
|
||
```js | ||
// 定义一个多行文本 | ||
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才能看到效果: | ||
|
||
```js | ||
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} 是一个整体,不可拆分。** | ||
|
||
# 2、正则修饰符:`g` | ||
|
||
接下来,进入本文的重点,修饰符 `g` 的匹配模式。 | ||
|
||
首先,我们先回到开头的那段代码,我想大部分人的答案可能是:`true true true true` 吧。 | ||
|
||
实际上,正确答案是:`true false true false`,是不是觉得不可思议?接着往下看。 | ||
|
||
## RegExp.prototype.test() | ||
|
||
`RegExp.prototype.test()` 方法的逻辑是:当找到第一个匹配项时,返回 `true`。查找整个字符串都没有找到匹配项,返回 `false`。 | ||
|
||
那具体又是如何查找的呢?这里我们就要看 `RegExp` 的另外一个方法了:`RegExp.prototype.exec()`。 | ||
|
||
注意观察这个方法的返回值: | ||
|
||
```js | ||
var r = /\d/; | ||
// 注意看,返回值是一个数组,除了匹配到的元素之外,还有一个 index 属性 | ||
console.log(r.exec('123')); // ["1", index: 0, input: "123"] | ||
``` | ||
|
||
关键就是 `exec` 返回值中的 `index` 属性,这个属性标识从输入文本的哪一个索引处开始查找匹配项。 | ||
|
||
如果不加 `g`,每次都是从索引0处开始查找。 | ||
|
||
```js | ||
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` 呢? | ||
|
||
```js | ||
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` 呢? | ||
|
||
还是先看代码: | ||
|
||
```js | ||
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`。** | ||
|
||
这也就解释了最开始的整个代码。 |