JavaScript正則表達式學習筆記(二) - 打怪升級

JavaScript正則表達式學習筆記(二) - 打怪升級.png

本文接上篇,基礎部分相對薄弱的同窗請移步《JavaScript正則表達式學習筆記(一) - 理論基礎》。上文介紹了8種JavaScript正則表達式的屬性,本文還會追加介紹幾種JavaScript正則表達式的屬性(注意是非標準屬性,但很好用)。javascript

一. 上文回顧

本文會用到上篇文章部份內容,因此簡單回顧下。前端

1.1 JavaScript正則表達式標誌符

  1. g: 全局匹配,即找到全部匹配的。對應屬性RegExp#globaljava

  2. i: 忽略字母大小寫。對應屬性RegExp#ingoreCase程序員

  3. m: 多行匹配,隻影響^和$,兩者變成行的概念,即行開頭和行結尾。對應屬性RegExp#multiline正則表達式

  4. u: ES6新增。含義爲「Unicode 模式」,用來正確處理大於\uFFFF的 Unicode 字符。也就是說,會正確處理四個字節的 UTF-16 編碼。對應屬性RegExp#unicode數組

  5. y: ES6新增。y修飾符的做用與g修飾符相似,也是全局匹配,後一次匹配都從上一次匹配成功的下一個位置開始。不一樣之處在於,g修飾符只要剩餘位置中存在匹配就可,而y修飾符確保匹配必須從剩餘的第一個位置開始,這也就是「粘連」的涵義。對應屬性RegExp#sticky框架

1.2 適用於Javascript正則表達式的方法

上篇文章《JavaScript正則表達式學習筆記(一) - 理論基礎》介紹了適用於JavaScript正則表達式模式匹配的相關API共有6種,RexExp提供2個,String提供4個,以下:函數

1. RegExp#test    // 適用於:驗證、提取
2. RegExp#exec    // 適用於:驗證、提取
3. String#search  // 適用於:驗證、提取
4. String#match   // 適用於:驗證、提取
5. String#split   // 適用於:切分
6. String#replace // 適用於:提取、替換
複製代碼

二. JavaScript正則表達式的四種操做

正則表達式是用於匹配字符串中字符組合的模式, 其核心內容即是模式匹配。也就是說,不論進行那種操做,首先要有模式匹配,有了模式匹配以後,才能進行驗證、替換、切分、提取這四種操做。工具

2.1 驗證

驗證應該是前端程序員寫正則表達式用的最多的方法吧,好比表單驗證之類的。能夠實現驗證的方法有4種。post

光說不練假把式,光練不說傻把式,又練又說纔是真把式💪。那麼,請看下面的例子🌰:

2.1.1 使用test方法

若是正則表達式與指定的字符串匹配 ,返回true,不然false。獲得的結果能夠直接使用。

const str = 'jing ke tong xue 666';

let reg1 = /\d{4}/;
let res1 = reg1.test(str);
console.log(res1);
// => false

let reg2 = /ke/;
let res2 = reg2.test(str);
console.log(res2);
// => true

let reg3 = /\d{3}/;
let res3 = reg3.test(str);
console.log(res3);
// => true
複製代碼

下面咱們看一個匹配身份證的示例:

const str1 = '411199909096896';
const str2 = '411425199909096896';
const str3 = '41142519990909689x';
const str4 = '41142519990909689X';
const reg = /^(\d{15}|\d{17}[\dxX])$/;
let res1 = reg.test(str1);
let res2 = reg.test(str2);
let res3 = reg.test(str3);
let res4 = reg.test(str4);
console.log(res1, res2, res3, res4);
// => true true true true
複製代碼

不過好在網上有不少可視化工具供咱們使用,看一下下圖可能就豁然開朗。

可視化視圖

看圖分析:這裏豎槓|的優先級最低,因此正則分紅了兩部分\d{15}\d{17}[\dxX]

  • \d{15} 表示15位連續數字

  • \d{17}[\dxX] 表示17位連續數字,最後一位能夠是數字,也能夠大寫或小寫字母 "x"

下面咱們再看一個稍微複雜一點的案例,匹配IPV4地址:

const str = '192.168.0.1';
let reg = /^((0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])\.){3}(0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])$/;
let res = reg.test(str);
console.log(res);
// => true
複製代碼

上面那個正則表達式初看起來有點嚇人哈🙈,這就是傳說中的寫正則不難,讀正則難。先看下面可視化視圖:

可視化視圖

此正則表達式主體結構大體以下:

((…)\.){3}(…)

兩個(...)是相同的內容,所以文字描述爲3位數字.3數字.3位數字.3位數字

下面咱們具體分析(...)也就是(0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5]),單獨看一下這段代碼的可視化視圖:

可視化視圖

看圖分析,它是一個多選結構,分紅5個部分:

  • 0{0,2}\d: 匹配一位數,包括 "0" 補齊的。好比,"9"、"09"、"009"

  • 0?\d{2}: 匹配兩位數,包括 "0" 補齊的,也包括一位數

  • 1\d{2}: 匹配 "100" 到 "199"

  • 2[0-4]\d: 匹配 "200" 到 "249"

  • 25[0-5]: 匹配 "250" 到 "255"

不要被看起來複雜的正則表達式嚇到,乍看起來複雜的不要不要的,那咱們就把它像洋蔥同樣,一層一層剝開它的心,擁抱它,而後扒光它。

2.1.2 使用exec方法

若是匹配成功,返回一個Array,不然返回null。獲得的結果兩次取反取得true或者false使用。

const str = 'jing ke tong xue 666';

let reg1 = /\d{4}/;
let res1 = reg1.exec(str);
console.log(res1);
console.log(!!res1);
// => null
// => false

let reg2 = /ke/;
let res2 = reg2.exec(str);
console.log(res2);
console.log(!!res2);
// => ["ke", index: 5, input: "jing ke tong xue 666", groups: undefined]
// => true

let reg3 = /\d{3}/;
let res3 = reg3.exec(str);
console.log(res3);
console.log(!!res3);
// => ["666", index: 17, input: "jing ke tong xue 666", groups: undefined]
// => true
複製代碼
2.1.3 使用search方法

若是匹配成功,返回正則表達式在字符串中首次匹配項的索引(大於等於0),不然,返回 -1。若是爲了和上面的方法保持一致返回true或false,這裏須要藉助一次按位非操做符(~)。

const str = 'jing ke tong xue 666';

let reg1 = /\d{4}/;
let res1 = str.search(reg1);
console.log(res1);
console.log(!!~res1);
// => -1
// => flase

let reg2 = /ke/;
let res2 = str.search(reg2);
console.log(res2);
console.log(!!~res2);
// => 5
// => true

const reg3 = /\d{3}/;
let res3 = str.search(reg3);
console.log(res3);
console.log(!!~res3);
// => 17
// => true
複製代碼
2.1.4 使用match方法

若是匹配成功,返回一個Array,不然返回null。獲得的結果兩次取反取得true或者false使用。

const str = 'jing ke tong xue 666';

let reg1 = /\d{4}/;
let res1 = str.match(reg1);
console.log(res1);
console.log(!!res1);
// => null
// => flase

let reg2 = /ke/;
let res2 = str.match(reg2);
console.log(res2);
console.log(!!res2);
// => ["ke", index: 5, input: "jing ke tong xue 666", groups: undefined]
// => true

let reg3 = /\d{3}/;
let res3 = str.match(reg3);
console.log(res3);
console.log(!!res3);
// => ["666", index: 17, input: "jing ke tong xue 666", groups: undefined]
// => true
複製代碼

2.2 替換

有時候找到了對象每每不是咱們最終的目的,找到了對象作點什麼纔是咱們想要的😍。

這裏咱們看一看找到後的替換操做,我本人的經常使用場景就是格式化日期和刪除空格。

// 把YYYY/MM/DD格式的日期替換成YYYY-MM-DD格式。
const str1 = 'jing-ke-tong-xue';

let res1 = str1.replace(/-/g, ' ');
console.log(res1);
// => jing ke tong xue

// 刪除先後空格, 爲了直觀這裏把先後空格替換成「刪除了的空格」
const str2 = ' jing ke tong xue ';
let res2 = str2.replace(/^\s|\s$/g, '刪除了的空格');
console.log(res2);
// => 刪除了的空格jing ke tong xue刪除了的空格

// 聽說下面這種方法速度比較快
const str3 = ' jing ke tong xue ';
let res3 = str3.replace(/^\s/, '刪除了的空格').replace(/\s$/, '刪除了的空格');
console.log(res3);
// => 刪除了的空格jing ke tong xue刪除了的空格
複製代碼

2.3 切分

切蘿蔔切蘿蔔切切切,包餃子包餃子捏捏捏🐷。在這裏,所謂切分就是把字符串切的一段一段的。

// 按空格切分
const str = 'jing ke tong xue';

console.log(str.split(/\s/));
// => ["jing", "ke", "tong", "xue"]

const str2 = 'jing * ke ¥ tong ^xue';
console.log(str2.split(/\s\*\s|\s¥\s|\s\^/))
// => ["jing", "ke", "tong", "xue"]
複製代碼

2.4 提取

有時候驗證、替換、切分都不是目的,咱們須要提取出來對咱們有用的信息,那麼就須要提取了。此時須要用到下篇會寫到的「括號」(分組引用或者分組捕獲)。

這裏有一個正則將會貫穿本小節,咱們先提早分析一下:

// 這裏爲了簡單測試,只考慮了日期格式,沒考慮日期的合理性
let reg = /(\d{4})\D(\d{2})\D(\d{2})/g;
複製代碼

可視化視圖:

可視化視圖

看圖說話:從上圖可知這段正則的意思就是:4個數字後跟一個非數字再跟2個數字再跟一個非數字再跟2個數字。形如:4個數字-2個數字-2個數字格式。

2.4.1 使用exec方法

const str = "提取日期測試字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期測試字符串";
// 這裏爲了簡單測試,只考慮了日期格式,沒考慮日期的合理性
let reg = /(\d{4})\D(\d{2})\D(\d{2})/g;
// 上篇文章有說:`exex`方法匹配到一次就會返回結果,想要下一個結果必須再次調用
console.log(reg.exec(str));
console.log(reg.exec(str));
// => ["2018-04-04", "2018", "04", "04", index: 16, input: "提取日期測試字符串,今天的日期是2018-04-04,今天的日期不是2018-40-40,提取日期測試字符串", groups: undefined]
// => ["6666-66-66", "6666", "66", "66", index: 34, input: "提取日期測試字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期測試字符串", groups: undefined]
複製代碼

2.4.2 使用match方法

const str = "提取日期測試字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期測試字符串";

// 這裏爲了簡單測試,只考慮了日期格式,沒考慮日期的合理性
let reg1 = /(\d{4})\D(\d{2})\D(\d{2})/;
console.log(str.match(reg1));
// 沒有g標識符返回結果與match無異
// => ["2018-04-04", "2018", "04", "04", index: 16, input: "提取日期測試字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期測試字符串", groups: undefined]

// 帶有g標識符,結果數組只包含匹配結果
// 這裏爲了簡單測試,只考慮了日期格式,沒考慮日期的合理性
let reg2 = /(\d{4})\D(\d{2})\D(\d{2})/g;
console.log(str.match(reg2));
// => ["2018-04-04", "6666-66-66"]
複製代碼

2.4.3 使用replace方法

在上篇文章步《JavaScript正則表達式學習筆記(一) - 理論基礎》的理論基礎中提到replace方法的第二個參數能夠是一個函數,函數接收的參數包含咱們須要的信息。

const str = "提取日期測試字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期測試字符串";

// 這裏爲了簡單測試,只考慮了日期格式,沒考慮日期的合理性
let reg1 = /(\d{4})\D(\d{2})\D(\d{2})/;

let res1 = [];
str.replace(reg1, function(match, year, month, day, offset, string) {
    res1.push(match, year, month, day, offset, string);
});
console.log(res1);
// => ["2018-04-04", "2018", "04", "04", 16, "提取日期測試字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期測試字符串"]

// 這裏爲了簡單測試,只考慮了日期格式,沒考慮日期的合理性
let reg2 = /(\d{4})\D(\d{2})\D(\d{2})/g;

let res2 = [];
str.replace(reg2, function(match, year, month, day, offset, string) {
    res2.push(match);
});
console.log(res2);
// => ["2018-04-04", "6666-66-66"]
複製代碼

從輸出效果來看,replace方法能夠達到模擬match方法的效果。這就是傳說中的***條條大路通羅馬***No roads can't lead to Rome(哼...哼哼...翻譯是我故意的嗷😋)

2.4.4 使用test方法

此方法會用到RegExp.$1-$9這一非標屬性,這裏只是使用,下文會作介紹。

const str = "提取日期測試字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期測試字符串";

// 這裏爲了簡單測試,只考慮了日期格式,沒考慮日期的合理性
let reg = /(\d{4})\D(\d{2})\D(\d{2})/;

reg.test(str);

// RegExp.$1-$9非標屬性,可是目的達到了,請勿用於生產環境
let res = [RegExp.$1, RegExp.$2, RegExp.$3];

console.log(res);
// => ["2018", "04", "04"]
複製代碼

2.4.4 使用search方法

此方法會用到RegExp.$1-$9這一非標屬性,這裏只是使用,一樣留到下文介紹。

const str = "提取日期測試字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期測試字符串";

// 這裏爲了簡單測試,只考慮了日期格式,沒考慮日期的合理性
let reg = /(\d{4})\D(\d{2})\D(\d{2})/;

str.search(reg);

// RegExp.$1-$9非標屬性,可是目的達到了,請勿用於生產環境
let res = [RegExp.$1, RegExp.$2, RegExp.$3];

console.log(res);
// => ["2018", "04", "04"]
複製代碼

到這裏正則表達式的驗證、替換、切分、提取已經介紹完了,有些操做是取巧的作法,也不建議在生產環境使用,不過某些特殊狀況除外。至於哪些狀況是特殊狀況,具體問題具體分析吧😄。

三. 注意要點

在本文 1.2 小節提到了6種能夠用於正則操做的方法,RegExp提供2種,String提供4種。本章節就圍繞這幾種方法展開。

3.1 match方法返回結果的格式不一致問題

這個問題上文《JavaScript正則表達式學習筆記(一) - 理論基礎》也有體現,這裏再單獨拿來講一說,以加深記憶。

const str = "提取日期測試字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期測試字符串";

// 這裏爲了簡單測試,只考慮了日期格式,沒考慮日期的合理性
let reg1 = /(\d{4})\D(\d{2})\D(\d{2})/;
console.log(str.match(reg1));
// => ["2018-04-04", "2018", "04", "04", index: 16, input: "提取日期測試字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期測試字符串", groups: undefined]

// 這裏爲了簡單測試,只考慮了日期格式,沒考慮日期的合理性
let reg2 = /(\d{4})\D(\d{2})\D(\d{2})/g;
console.log(str.match(reg2));
// => ["2018-04-04", "6666-66-66"]

// 這個正則只是來客串說明問題,沒有其餘意義
let reg3 = /.^/;
console.log(str.match(reg3));
// => null

// 這個正則只是來客串說明問題,沒有其餘意義
let reg4 = /.^/g;
console.log(str.match(reg4));
// => null
複製代碼
  • 當沒有g標識符時,返回的結果爲標準匹配格式,包含完整的匹配信息。

  • 當有g標識符時,返回的結果爲全部匹配結果組成的數組。

  • 當匹配不成功時,不管有沒有標識符(包括igmyu的任意組合),都返回null

3.2 searchmatch的參數問題

話很少說,先看代碼:

// 爲了說明問題,日期格式選擇了用「.」鏈接,由於「.」在正則中屬於元字符
const str = '2018.04.04';

console.log(str.search('.'));
console.log(str.search(/./));
console.log(str.search('\\.'));
console.log(str.search(/\./));
// => 0
// => 0
// => 4
// => 4

console.log(str.match('.'));
console.log(str.match(/./));
console.log(str.match('\\.'));
console.log(str.match(/\./));
// => ["2", index: 0, input: "2018.04.04", groups: undefined]
// => ["2", index: 0, input: "2018.04.04", groups: undefined]
// => [".", index: 4, input: "2018.04.04", groups: undefined]
// => [".", index: 4, input: "2018.04.04", groups: undefined]

console.log(str.replace('.', '-'));
console.log(str.replace( /./, '-'));
console.log(str.replace('\.', '-'));
console.log(str.replace(/\./, '-'));
// => 2018-04.04
// => -018.04.04
// => 2018-04.04
// => 2018-04.04

console.log(str.split('.'));
console.log(str.split(/./));
console.log(str.split('\\.'));
console.log(str.split(/\./));
// => ["2018", "04", "04"]
// => ["", "", "", "", "", "", "", "", "", "", ""]
// => ["2018.04.04"]
// => ["2018", "04", "04"]
複製代碼

從上述代碼能夠看出search方法和match方法會將接收到的字符串參數轉爲正則,而replace方法和split方法不會轉換,因此使用時請注意。

3.3 execmatch的王者之爭

王者之爭第一回合:

const str = '2018-04-04';

let reg1 = /\b(\d+)\b/;
let reg2 = /\b(\d+)\b/g;

console.log(reg1.exec(str));
console.log(reg2.exec(str));
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]

console.log(str.match(reg1));
console.log(str.match(reg2));
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]
// ["2018", "04", "04"]
複製代碼
  • g標識符對exec方法沒有產生影響,可是改變了match方法的行爲。沒主見啊,沒主見。

  • 沒有g標識符時,match返回標準匹配參數,有g標識符時返回了匹配結果的集合。可是對於exec方法不管有沒有g標識符都返回了一樣的結果。不智能啊,不智能。

這一回合實力相差不大,看不出孰優孰劣。

王者之爭第二回合:

const str = '2018-04-04';

let reg1 = /\b(\d+)\b/;
let reg2 = /\b(\d+)\b/g;

console.log(reg2.lastIndex);
console.log(reg2.exec(str));
// => 0
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]

console.log(reg2.lastIndex);
console.log(reg2.exec(str));
// => 4
// => ["04", "04", index: 5, input: "2018-04-04", groups: undefined]

console.log(reg2.lastIndex);
console.log(reg2.exec(str));
// => 7
// => ["04", "04", index: 8, input: "2018-04-04", groups: undefined]

console.log(reg2.lastIndex);
console.log(reg2.exec(str));
// => 10
// => null

// 上一次匹配失敗,這一次從頭開始
console.log(reg2.lastIndex);
console.log(reg2.exec(str));
// => 0
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]

// 沒有g標識符時match方法每次都從第一位開始匹配
console.log(str.match(reg1));
console.log(str.match(reg1));
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]
複製代碼

這一回合沒有懸念,exec方法勝出。不過上面的寫法未免太過繁瑣。請看下面當exec趕上while

const str = '2018-04-04';

let reg = /\b(\d+)\b/g;

// 結合while流程控制語句
let res;
while (res = reg.exec(str)) {
    console.log(reg.lastIndex, res);
}
// => 4 ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]
// => 7 ["04", "04", index: 5, input: "2018-04-04", groups: undefined]
// => 10 ["04", "04", index: 8, input: "2018-04-04", groups: undefined]
複製代碼

王者之爭至此結束。exec要比match強大那麼一點哈。

3.4 除了exec方法,g標識符對test方法也有影響

上篇文章曾經提到 lastIndex 是正則表達式的一個可讀可寫的整型屬性,用來指定下一次匹配的起始索引。 也就是說,只要正則仍是那個正則,當存在g標識符的時候lastIndex都會作出相應的改變,要匹配的字符串能夠不是同一個。

const str1 = '2018-04-04';
const str2 = '2018-04-05';
const str3 = '2018-04-06';
const str4 = '2018-04-07';
const str5 = '2018-04-08';

let reg = /\b(\d+)\b/g;

console.log(reg.lastIndex);
console.log(reg.test(str1));
// => 0
// => true

console.log(reg.lastIndex);
console.log(reg.test(str2));
// => 4
// => true

console.log(reg.lastIndex);
console.log(reg.test(str3));
// => 7
// => true

console.log(reg.lastIndex);
console.log(reg.test(str4));
// => 10
// => false

console.log(reg.lastIndex);
console.log(reg.test(str5));
// => 0
// => true
複製代碼

當沒有g操做符時,始終從0開始匹配,這裏就不作演示了。

3.5 split方法注意事項

const str = '2018-04-04';

console.log(str.split('-'));
console.log(str.split('-', 2));
console.log(str.split('-', 10));
// => ["2018", "04", "04"]
// => ["2018", "04"]
// => ["2018", "04", "04"]

let reg1 = /-/;
let reg2 = /(-)/;

console.log(str.split(reg1));
console.log(str.split(reg2));
// => ["2018", "04", "04"]
// => ["2018", "-", "04", "-", "04"]
複製代碼
  • split方法能夠接收第二個參數指定返回數組長度,第二個參數只有小於實際返回數組長度時才生效。

  • split接收的正則表達式種包含分組模式時,返回的結果數組包含分組匹配項。

3.6 強大的replace方法

replace方法不但能夠接收一個函數做爲第二個參數(前面已經體現,這兒不重複示例),也能夠接收一個字符串做爲第二個參數。此處的字符串除了是一個普通的替換字符串以外,也能夠是一個特殊變量。本小節以實際示例介紹一下這幾個特殊變量(第一篇理論基礎有說起)。

變量名 表明的值
$$ 插入一個 "$"。
$& 插入匹配的子串。
$` 插入當前匹配的子串左邊的內容。
$' 插入當前匹配的子串右邊的內容。
$n 匹配第n個分組裏捕獲的文本,n是不大於100的正整數。

哇哈,是時候表演真正的技術了,下面咱們就來看看replace的能力。

const str1 = '3 6 9';
        
let reg1 = /\d/g;
// 被$包圍
console.log(str1.replace(reg1, '$$$&$$'));
// => $3$ $6$ $9$

// 分身
console.log(str1.replace(reg1, '$&$&$&'));
// => 333 666 999

// 分身相加
let reg2 = /(\d)\s(\d)\s(\d)/;
console.log(str1.replace(reg2, '$1$1$1+$2$2$2$2=$3$3$3'));
// => 333+6666=999

// 你愛我我愛你
console.log(str1.replace(reg2, '$1$1$1+$2$2$2=$2$2$2+$1$1$1=$3$3$3=>😍'));
// => 333+666=666+333=999=>😍

const str2 = '3🥕6🌰9';
let reg3 = /🥕|🌰/g;
console.log(str2.replace(reg3, "($&的左邊是: $`, 右邊是: $')"));
// => 3(🥕的左邊是: 3, 右邊是: 6🌰9)6(🌰的左邊是: 3🥕6, 右邊是: 9)9
複製代碼

忽然間感受王者之爭不該該只讓execmatch參加,replace這麼強大,也應該參與其中的嘛😄。

3.7 構造函數和字面量問題

這裏沒有什麼懸念,通常建議優先使用字面量的方式建立正則表達式,由於構造函數中須要對元字符轉義,會多寫不少的反斜槓。固然特殊狀況仍是要用構造函數。

const str = '2018-04-04';

let reg1 = /(\d{4})\D(\d{2})\D(\d{2})/;
console.log(reg1);
console.log(reg1.test(str));
// => /(\d{4})\D(\d{2})\D(\d{2})/
// => true

let reg2 = new RegExp('(\d{4})\D(\d{2})\D(\d{2})');
console.log(reg2);
console.log(reg2.test(str));
// => /(d{4})D(d{2})D(d{2})/
// => false

let reg3 = new RegExp('(\\d{4})\\D(\\d{2})\\D(\\d{2})');
console.log(reg3);
console.log(reg3.test(str));
// => /(\d{4})\D(\d{2})\D(\d{2})/
// => true
複製代碼

下面是特殊狀況:

let name = 'user name';

// user name是一個變量
const str = '2018-04-04 user name';

// 在字面量中,沒法實現動態拼接
let reg1 = /(\d{4})\D(\d{2})\D(\d{2})\D + name/;
console.log(reg1);
console.log(reg1.test(str));
// => /(\d{4})\D(\d{2})\D(\d{2})\D + name/
// => flase

let reg2 = new RegExp('(\\d{4})\\D(\\d{2})\\D(\\d{2})\\D' + '(' + name + ')');
console.log(reg2);
console.log(reg2.test(str));
// => /(\d{4})\D(\d{2})\D(\d{2})\D(user name)/
// => true
複製代碼

3.8 幾個非標屬性

上面用到了RegExp.$1這一非標屬性,所謂非標屬性,就是此屬性不符合當前任何標準規範。因此,請儘可能不要在生產環境中使用,除非特殊狀況而且你能保證之後也不會出錯。

屬性 別名 說明
RegExp.$1-$9 靜態、只讀屬性。包含括號子串匹配的正則表達式的靜態和只讀屬性。只有在正確匹配的狀況下才會改變。雖然括號能夠無限,可是此屬性最多隻能匹配9個。
RegExp.input RegExp.$_ 靜態屬性,含有正則表達式最近一次所匹配的字符串。當正則表達式上搜索的字符串發生該變,而且字符串匹配時,input 屬性的值會修改。
RegExp.lastMatch RegExp['$&'] 靜態、只讀屬性。含有最近一次匹配到的字符串。屬性的值是隻讀的,會在匹配成功時修改。
RegExp.lastParen RegExp['$+'] 靜態、只讀屬性,包含匹配到的最後一個子串。會在匹配成功時修改。
RegExp.leftContext RegExp['$`'] 靜態、只讀屬性。含有最新匹配的左側子串。 會在匹配成功時修改。
RegExp.rightContext RegExp["$'"] 靜態、只讀屬性。含有最新匹配的右側子串。 會在匹配成功時修改。

這幾個屬性平時也基本用不到,瞭解瞭解老是好的,請看下面示例:

const str = 'a1b2c3d4e5f6';
let reg = /([a-f])([1-6])/g;

// 爲了倒數第二個有輸出,這裏執行兩次exec方法
console.log(reg.exec(str));
console.log(reg.exec(str));
// ["a1", "a", "1", index: 0, input: "a1b2c3d4e5f6", groups: undefined]
// ["b2", "b", "2", index: 2, input: "a1b2c3d4e5f6", groups: undefined]

console.log(RegExp.$1);
console.log(RegExp.$2);
// => b
// => 2

console.log(RegExp.input);
console.log(RegExp.$_);
// => a1b2c3d4e5f6
// => a1b2c3d4e5f6

console.log(RegExp.lastMatch);
console.log(RegExp['$&']);
// => b2
// => b2

console.log(RegExp.lastParen);
console.log(RegExp['$+']);
// => 2
// => 2

console.log(RegExp.leftContext);
console.log(RegExp['$`']);
// => a1
// => a1

console.log(RegExp.rightContext);
console.log(RegExp["$'"]);
// => c3d4e5f6
// => c3d4e5f6
複製代碼

四. 奇技淫巧

本文寫的也挺長的,剩下的準備再寫一篇終結JavaScript正則表達式部分的內容。那麼本文就先用一個真是案例來作結尾吧。

JavaScript經常使用的類型判斷實現

下面這段代碼是在某個框架源碼中見到的,初見之時倍感驚豔(原諒我入行不久見識短🍷),其中也用到上文提到的split方法,特拿出來分享一下。

let utils = Object.create(null);
const types = 'Boolean|Number|String|Function|Array|Date|RegExp|Object|Error';
types.split('|').forEach(type => {
    utils['is' + type] = obj => {
        return Object.prototype.toString.call(obj) == '[object ' + type + ']';
    };
});
console.log(utils.isBoolean('true'));
console.log(utils.isBoolean(true));
複製代碼

雖然能夠將Boolean|Number|String|Function|Array|Date|RegExp|Object|Error存儲爲數組減小一次split切分操做,可是這樣彷佛多了點黑科技的感受。

因爲本同窗能力有限,不足之處還望各位大佬同窗指正。

至此,本文完。

相關文章
相關標籤/搜索