你真的理解正則修飾符嗎?

0、前言

一段代碼引起的思考:正則表達式

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 RegExpprototype

它們的含義以下:code

  • global(g) 針對字符串中全部可能的匹配來測試正則表達式
  • ignoreCase(i) 匹配時忽略大小寫
  • multiline(m) 多行輸入將被視爲多行(此時開始^和結尾匹配$能夠在每行中進行匹配)
  • unicode(u) 對字符串採用unicode進行匹配

修飾符: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()

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

這也就解釋了最開始的整個代碼。

相關文章
相關標籤/搜索