【愣錘筆記】一支穿雲箭、正則來相見

愣錘發表於2019-05-22

有人說,一行正則抵得上100行程式碼……

正規表示式,每門語言都有,在我們的js開發中,最常見的使用場景:一是表單驗證,像是登入註冊啊,使用者輸入檢測啊,不管在前臺專案還是後臺管理系統,都會經常使用;二是,在開發一些重資料互動的後臺管理系統或者應用的時候,更是會大量的使用,各種字元匹配,表示式檢測等等。

正則本身知識點不是很多,但是由於各種字元意思容易忘記,所以需要經常複習,多使用就記住了。

建立一個正規表示式:

// 通過RegExp建構函式建立
var regex1 = new RegExp('[a-z]', 'g');
// 通過直接字面量建立
var regex2 = /[a-z]/g;複製程式碼

這兩種方式都可以建立正規表示式,以上兩種方式建立了一個等價的正規表示式,均為匹配字母a到z。但是日常開發中,基本都是通過直接字面量建立:兩個反斜槓之間為正規表示式,反斜槓後面跟著修飾符i、g、m。修飾符可寫可不寫,也可以同時寫多個,他們的意思分別是:

i:表示忽略大小寫,就是在字串匹配的時候不區分大小寫。
g:表示全域性匹配,即所有滿足的字元都會被匹配到,一直匹配到字串的結尾。
m:表示進行多行匹配。這個是什麼意思呢,舉個例子吧:

var str = 'hello world \n my name is LengChui.'複製程式碼

可看到這個字串中有一個\n換行符,正常匹配時,到\n就結束了,也就是隻會匹配到這一行的結尾。但是如果加了m修飾符,會繼續往後面匹配,一直匹配到字串的最終結尾。

當然了,修飾符可以同時寫多個,例如:var regex = /[a-z]/ig;表示匹配所有的字母a到z,且不區分大小寫。這樣,對於字串'a123H'就會匹配到a和Z


正規表示式方法:test()和exec()

所有的正規表示式都有test和exec這兩個方法。

test()方法:測試字串中是否包含了匹配該正規表示式的子串,如果包含了這樣的子串,那麼返回true,否則返回false

// 定義一個匹配hello字串的正規表示式
var reg = /hello/g;
var str = 'hello world';
// 如果字串str中匹配到了hello字串,則列印如下資訊
if (reg.test(str)) {
    console.log('字串str中含有hello子串')
}複製程式碼

這個方法相信在驗證使用者輸入內容的時候,會經常使用到。例如,相信很多人寫過類似下面的正則驗證使用者輸入值的合法性程式碼:

// 點選提交按鈕,驗證輸入值,提交ajax請求
document.getElementById('submitButton').addEventListener('click', function (event) {
    // 獲取使用者輸入的手機號
    var phoneNumber = document.getElementById('phone');
    // 驗證手機號格式的正則
    var regPhone = /^1[3|4|5|8]\d{9}$/g;
    // 檢測輸入的合法性,進行錯誤提示
    if (!phoneNumber) {
        $tim('請輸入手機號!')
        return;
    }
    if (!regPhone.test(phoneNumber)) {
        $tip('手機號格式不正確,請重新輸入!');
        return;
    }
    // 如果驗證成功,提交ajax請求
    $.ajax(………………)
}, false);複製程式碼


exec方法:接收一個引數,即待檢測的字串。它返回一個增強的陣列物件,陣列的第一項為匹配到的字串。如果正規表示式中含有子表示式,那麼該陣列後續的項依次為匹配到的第一個子表示式的匹配結果,第二個……第n個。如果沒有匹配到任何內容,則返回null。

通過例子來說明exec的使用方法:

// 定義一個字串
var str = 'hello javascript, hello javaspring'
// 定義一個正規表示式,該表示式匹配任意java後跟任意大小寫字母的字串
// 注意這裡的正規表示式沒有加g,後面會說明exec的正規表示式加g與不加g的區別
var reg = /java([a-zA-Z]+)/;
// 呼叫exec方法
var execResult = reg.exec(str);
console.log(execResult)複製程式碼

看下最終的列印結果:

【愣錘筆記】一支穿雲箭、正則來相見

可以看到返回一個陣列,陣列的第一項為匹配到的結果,第二項為第一個子表示式的匹配結果,如果還有其他子表示式,會依次往後排。同時這個陣列物件還有以下幾個屬性:

  1. groups: undefined
  2. index// 當前匹配結果的開始下標。這裡匹配的javascript字串的開始下標為6。
  3. input // 待匹配的字串,其實就是我們的這裡的str字串

注意上述正規表示式/java([a-zA-Z]+)/沒有加修飾符g,這意味著不需要全域性匹配。這種情況下,exec()方法只會匹配依次便最終匹配結束了,那麼如果繼續呼叫execResult = reg.exec(str);,則會重新開始匹配,即從字串0的位置開始重新匹配並返回新的匹配結果。其結果肯定是一樣的。如下面例子:

var str = 'hello javascript, hello javaspring'
var reg = /java([a-zA-Z]+)/;
// 第一次匹配呼叫並列印結果
var execResult = reg.exec(str);
console.log(execResult);
// 重新呼叫並列印結果
execResult = reg.exec(str);
console.log(execResult);複製程式碼

【愣錘筆記】一支穿雲箭、正則來相見

可以看到兩次呼叫的匹配結果是一樣的。這裡不禁就有人說了,那不是廢話了,呼叫同一個表示式,結果不就一樣嘛。然而,還真不是。在介紹這個之前,我們先簡單提一下正規表示式的例項屬性,告訴了我們該正規表示式的一些基本資訊。這個不需要記,瞭解一下即可:

var reg2 = /a/igm; // 定義一個不區分大小寫、全域性匹配、多行匹配字串a的正規表示式
console.dir(reg2) // 列印出該物件複製程式碼

【愣錘筆記】一支穿雲箭、正則來相見

下表中列出了所有的正規表示式例項屬性。

例項屬性含義
globalBoolean值,指明正規表示式是否包含g修飾符
ignoreCaseBoolean值,指明正規表示式是否包含i修飾符
lastIndex
若正規表示式包含g修飾符,該屬性指明執行exec()test()方法後,最後一次匹配結果字串後面的第一個字元的位置
source只讀屬性,給出了正規表示式的內容,即除了斜槓和選項字元之外的整個正規表示式。
multilineBoolean值,指定搜尋字串時是否跨行搜尋,即是否包含m修飾符,該屬性的另外一個名稱是$*

瞭解了這個之後,我們回過頭來繼續說exec()方法。在呼叫exec()方法的正規表示式沒有加修飾符g,即不是全域性匹配的模式下,每次呼叫exec()之後,本次的正則匹配也就最終結束了,注意是最終結束了。下次該正規表示式再次呼叫exec()方法時會從字串開頭開始匹配,可以立即為開始了一次新的正則匹配。

那麼如果呼叫exec()方法的正規表示式是全域性匹配的話(加了修飾符g),該正規表示式每次呼叫exec()方法結束後,其例項屬性lastIndex都會指向本次匹配結果字串後面一個字元的位置,直到匹配結果返回null,即沒有匹配到任何結果的時候,才會將其lastIndex屬性重置為0。繼續看這個例子演示:

var str = 'hello javascript, hello javaspring'
// 定義匹配任何java後跟任意大小寫字母的字串,注意這裡是全域性匹配
var reg = /java([a-zA-Z]+)/g;

// 該正規表示式第一次呼叫exec()方法並列印匹配結果和其例項屬性lastIndex
var execResult = reg.exec(str);
console.log(execResult, reg.lastIndex);

// 第二次呼叫
execResult = reg.exec(str);
console.log(execResult, reg.lastIndex);

// 第三次呼叫
execResult = reg.exec(str);
console.log(execResult, reg.lastIndex);複製程式碼

【愣錘筆記】一支穿雲箭、正則來相見

從列印結果可以看出,第一次匹配到了字串javascript,其開始下標為6,例項屬性lastIndex16,即javascript字串後面的一個字元“逗號”的位置下標。第二次呼叫的匹配結果為javaspringlastIndex的值為34。第三次呼叫沒有匹配到任何結果,所以返回了null,其lastIndex的結果也被重置為了0

關於正則方法exec()有一點必須要提,很容易導致錯誤的情況。當呼叫exec()方法的正規表示式是全域性匹配的情況下,對一個字串匹配後,如果沒有匹配到最終結果,即沒有返回null的情況下,使用該正規表示式對新字串進行exec()方法呼叫時,切記一定要先將該正規表示式的例項屬性lastIndex重置為0。看例子:

var str = 'hello javascript, hello javaspring'
var str2 = 'javascript and html'

// 定義匹配任何java後跟任意大小寫字母的正規表示式,全域性匹配
var reg = /java([a-zA-Z]+)/g;

// 使用該正則的exec()方法匹配字串str
var execResult = reg.exec(str);
console.log(execResult, reg.lastIndex);

// 使用該正則的ecev()方法匹配字串str2
execResult = reg.exec(str2);
console.log(execResult, reg.lastIndex);複製程式碼

【愣錘筆記】一支穿雲箭、正則來相見

從列印結果可以看出,對於str字串的正則匹配時,正常返回了結果,其lastIndex的值為16,還並沒有最終匹配結束,lastIndex值也沒有重置為0。這時候直接使用該正規表示式對str2進行exec()方法呼叫,卻返回了null。為什麼呢,正常應該也可以匹配到str2字元的javascript字元啊。這是因為,對str呼叫後,其lastIndex值被賦值為了16,那麼下次對str2呼叫的時候,便從下標16開始呼叫,str2中從下標16開始匹配自然而然是沒有匹配到任何字串。注意,正規表示式的例項屬性lastIndex是可讀可寫的屬性,所以為了避免這種情況,在對str2正則匹配前,先將其lastIndex屬性重置為0

reg.lastIndex = 0;
execResult = reg.exec(str2);
console.log(execResult, reg.lastIndex);複製程式碼

【愣錘筆記】一支穿雲箭、正則來相見

元字元

所謂元字元,其實就是在正規表示式的不同上下文中有著特殊意義的標點符號,請看下面:

. * + ( ) $ / \ ? [ ] ^ { } - ! < >       複製程式碼

嗯~沒錯,就是這幾個字元,通過不同的組合可以幫我們構建出非常強大的正規表示式。注意,這些字元帶有特殊意義,如果就只是單純的項匹配這些字元,需要加/進行轉義,例如\.就只是匹配一個點,沒有其他特殊意義。下面我們一一進行介紹。

. : 匹配除了換行符和回車之外的任何字元,eg: /./ 匹配一個除換行回車之外的任意字元;

[] :  匹配方括號裡面的任意字元。方括號內可以是1個字元也可以是多個字元。eg: [ab] 匹配字元a,也可以匹配字元b;[a- z] 可以匹配字母a到z任意一個,-表示的意思;[0-9]可以匹配數字0到9;也可以混合使用[a-zA-Z0-9]可以匹配小寫字母a到z,大寫字母A-Z,可以匹配數字0-9;

[^] : 匹配除了方括號內^後面的任意字元,可以理解為[]的取反操作。eg: /[^ab]/ 匹配除了a和b以外的任意字元。 

| : 或,即匹配 | 左邊或者右邊的表示式。eg: /a|b/ 匹配字元a或者字元b

: 匹配0個或多個前面的表示式, eg: /a*/ 匹配0或者多個a,a、aa、aaa、aaaa等都是滿足匹配規則的。

+ : 匹配1個或多個前面的表示式,和*類似,但是至少要滿足匹配一次。eg: /a+/ 可以匹配1個或多個a,隱藏a、aa、aaa等都是合法的,但是bcdf是不符合匹配規則的,因為沒有至少匹配到一個a。

? :  匹配0和或1個前面的表示式,可以理解為前一項是可選的。和*+類似,但是要注意三者的區別。

{n, m} : 匹配前一項n到m次,包含n次和m次。eg: /[0-9]{6,8}/ 可以匹配6到8位數字。

{n, } :  匹配前一項至少n次。 eg: /[0-9]{11, }/ 至少匹配11位數字

{n} : 匹配前一項n次。 eg: /[0-9]{5}/  匹配5位數字

需要注意的是:*?可以匹配前一項0次,因此像類似 /a*/.test('bcdf') 是返回true的,因為他匹配到了0個a;而/a*/.exec('bcdf')也是返回了一個第一項是一個空字串的陣列,而不是返回null,也是因為匹配了0個a。

另一個需要注意的是,正則匹配預設都是貪婪匹配。什麼意思呢?就是儘可能多的匹配,比如用/a+/來匹配字串aaabbbb時,會匹配到aaa,即儘可能多匹配。類似的重複匹配時都是貪婪匹配的。然而可以使用這些元字元加上?的形式來取消貪婪匹配,即儘可能的少匹配。如/a+?/再對aaabbbb匹配時,則只會匹配到一個a,其他的*?  {}? ??同理。

\ : 和一些字元組合使用會有特殊的意義:

\w元符號,等價於[a-zA-Z0-9_],匹配任何字母、數字、下劃線字元,這樣的字元也稱為單詞字元
\W元符號,等價於[^a-zA-Z0-9_],匹配除了字母、數字、下劃線字元之外的任何字元
\d元符號,等價於[0-9],匹配任何單個的數字字元
\D元符號,等價於[^0-9],匹配除了數字之外的任何單個字元
\s元符號,匹配空白字元,即空格、Tab字元和回車換行符
\S元字元,匹配任何非空白字元

^ :匹配字串的開始位置,或多行匹配模式中(即加了修飾符m)每一行的開始位置

$ : 匹配字串的結束位置,或多行匹配模式下(即加了修飾符m)每一行的結束位置

^$這兩個元字元,在日常開發中幾乎是最常見的了,幾乎大部分的正規表示式都是這種形式的:/^表示式$/。都知道是它的意思是匹配字串的開始和結束為止,猛一看好像理解了,但是細細揣摩貌似又不知道到底是什麼意思。下面我就細細說來:

這兩個字元和上面其他元字元不同的是,這個字元匹配的是位置,而不是具體匹配的某個字元。這麼說,應該清晰一點了,所以/^表示式$/就是從字串開頭的位置開始,一直到結束的位置,都必須滿足匹配規則才行。再次強調下,^$匹配的都只是一個位置,位置,位置。

\b : 匹配單詞邊界。這個也是用來匹配位置的,通俗的講,就是匹配區分單詞分割位置的。例如,兩個單詞之間的位置,第一個單詞前面的位置,最後一個單詞後面的位置,都是匹配的單詞位置。eg: /\bjava\b/可以匹配單詞java,即匹配java字元兩邊都是單詞邊界的結果,對於字串I love java very much則匹配成功,而字串I love javascript very much則匹配不成功。因為第一句的java兩邊空格都是單詞邊界,而javascipt雖然包含了java單詞,但是隻滿足左邊是單詞邊界,其子串java的右邊是script子串,而不是單詞邊界,所以不滿足兩邊都是單詞邊界的匹配規則。

console.log(/\bjava\b/.test('java')) // true
console.log(/\bjava\b/.test('javascript')) // false複製程式碼

\B:匹配非單詞邊界,是\b的反義詞。eg:/\Bscript/ 對於字串javascript是可以匹配成功的,因為其子串script的左側是java字串,不是單詞邊界。而對於字串script則是不成功的,因為其左側什麼都沒有,即左側就是一個單詞邊界。

console.log(/\Bscript/.test('javascript')) // true
console.log(/\Bscript/.test('script')) // false複製程式碼

【愣錘筆記】一支穿雲箭、正則來相見

斷言

js裡面的斷言只支援先行斷言,又分為正向先行斷言負向先行斷言。這麼說比較繞口,讓人云裡霧裡的感覺,所以還是直接上例子:

?= : 正向先行斷言,其實就是說?=左邊的內容後面,必須跟著?=右邊的內容。一般使用方式為:x(?=y),意思是字元x後面必須是字元y。eg:/java(?=script)/ 只能匹配javascript,不能匹配javaspring,java123等等。

?! : 負向先行斷言,即?!左邊的內容後面一定不能跟?!右邊的內容。一般使用方式為: x(?!y),意思是字元x後面一定不能是字串y。eg:/java(?=script)/ 只能匹配javaspring,java123等等,但不能匹配javascript。


分組/子表示式

() : 子表示式,也可以叫分組,就是把括號裡面的表示式作為一個整體來處理。一個正規表示式裡面可以有多個子表示式,子表示式裡面也可以巢狀子表示式。在我們介紹exec()方法的時候說過,exec()返回一個增強的陣列,陣列第一項為該正規表示式的匹配結果,第二項及以後為對應的子表示式的匹配結果。

// 沒有子表示式的正則exec()方法
var reg1 = /[a-z][0-9]/;
var str1 = 'hello123';
console.log(reg1.exec(str1))

// 新增了子表示式的正則exec()方法		
var reg2 = /([a-z])([0-9])/;
var str2 = 'hello123';
console.log(reg2.exec(str2))

// 子表示式巢狀的情況
var reg3 = /(([a-z])([0-9]))/;
var str3 = 'hello123';
console.log(reg3.exec(str3))
複製程式碼

【愣錘筆記】一支穿雲箭、正則來相見

列印結果可以看出,加子表示式之後,比沒加之前多返回了幾項,這幾項分別對應每個子表示式匹配的結果。注意,當有巢狀的情況下,子表示式的順序,其實就是所有的左括號從左到右出現的順序。

每一子表示式匹配到的值都會被儲存下來,分別存在\1,\2,\3……裡面,也會儲存在$1,$2,$3……兩者的區別是,\1這種型別的是使用在正規表示式中的,$1這種型別的是使用在字串的方法中。關於字串方面會在文章後面的內容講解。先看個例子吧:

// 需要匹配第一第二位都是數字,第三位和第一位必須相同,第四位和第二位必須相同的正規表示式
var reg4 = /(\d)(\d)\1\2/;
// 列印為true,因第一位和第二位都是數字,第三位和第一的值相同,第四位和第二位的值也相同
console.log(reg4.test('1212')) // true
// 列印結果為false,因為第三位的值和第一位的不一樣
console.log(reg4.test('1232'))
複製程式碼

從這正規表示式可以看到,要求匹配的第一位和第二位都是數字,那麼後面的\1和\2是什麼意思呢,就是說\1就是第一個(\d)匹配到的結果,\2就是第二個(\d)匹配到的結果。注意,這裡的意思並不是說第三位第四位是和第一第二位匹配的規則相同的,而是說,加入第一位匹配到了數字2,那麼第三位也只能數字2,如果第二位匹配到了數字4,那麼第四位也必須是4才可以。那麼最終的匹配結果就是類似1212, 4747等。eg: 左引號和右引號必須相匹配的情況,可以這樣子:/['"][^'"]*\1/,即首先是單引號或者雙引號,後面是0到多個非單引號雙引號字元,最後如果第一個匹配了單引號則最後一個必須是單引號,如果第一個匹配到了雙引號則最後也必須是雙引號。

注意在子表示式巢狀的情況下,\1,\2……對應的其實就是左括號的值,不管它怎麼巢狀。這麼說應該更好理解了。

那麼問題來了,如果不想儲存子表示式的值呢,或者說多個子表示式中我不想儲存某些子表示式的值怎麼辦呢?

(?:)  : 一個問號加一個冒號,成為無記憶匹配。他依然是對括號中裡面的內容進行分組,但是不會捕獲子模式,即不會儲存匹配到的值到\1,\2,\3……中。現在我修改一下上面的例子再看:

// 需要匹配第一第二位都是數字,第三位和第二位必須相同
var reg4 = /(?:\d)(\d)\1/;
// 列印為false,因第一位和第二位都是數字,第三位和第二位的值不一樣
console.log(reg4.test('1212'))
// 列印結果為true,因為第三位的值和第二位的值相同
console.log(reg4.test('1232'))複製程式碼

從表示式可以看出,我們的第一個(?:\d)子表示式新增了無記憶匹配模式,所以後面的\1裡面存放的值就變成了後面的(\d)匹配的結果。因此,子表示式設定了無記憶匹配模式後,\1,\2……便不會再儲存其匹配結果了,而是儲存後面沒有設定無記憶匹配模式的子表示式。


字串方法中正規表示式的運用

字串方法小夥伴們都很熟悉,這裡只說和正則匹配相關的方法。正規表示式本身就是用來處理字串的匹配規則,那麼相應的字串方法,當然得和正則勾搭點關係啦~~好了,拉回正題,下面先列舉和正則有關的字串方法:

match(regex)返回所有與正規表示式regex相匹配的子串陣列
replace(regex,replacement)字串替換。將字串中的regex指定的子串替換為子串replacement
search(regex)字串搜尋。查詢正規表示式regex指定的模式在字串中的開始位置
split(regex)字串分割。使用正規表示式regex分割字串,並將分割的結果以陣列的形式返回

  (1) match方法:返回一個由匹配結果組成的陣列,如果沒有匹配到任何結果則返回null。該方法接收一個正規表示式作為引數,如果不是正規表示式,它會首先呼叫new RegExp()將引數轉換成正規表示式。

注意:如果正則引數不帶修飾符g,match方法不會進行全域性匹配,其效果和不加修飾符g的exec()方法返回的結果是一樣的。如果忘記了exec()方法,可以往回翻翻回顧一下。

'aaaa'.match('b') // 返回null,因為沒有匹配到任何內容複製程式碼

下面看下正規表示式不帶修飾符g的例子:

console.log('abcd?'.match(/a/)) 複製程式碼

【愣錘筆記】一支穿雲箭、正則來相見

在不是全域性匹配的情況下,其返回結果和exec()方法如出一轍,如果表示式含有子表示式的話,陣列的第二項及後續項是子表示式的匹配結果。如果是全域性匹配的話,則會返回一個由所有匹配結果組成的陣列:

// 不含有子表示式的全域性匹配
console.log('1a2b3c4d5?'.match(/[0-9]/g))

// 含有子表示式的全域性匹配
console.log('1a2b3c4d5?'.match(/[0-9]|(a)/g))複製程式碼

【愣錘筆記】一支穿雲箭、正則來相見

結果非常的直觀,在全域性匹配模式下會返回所有匹配結果組成的陣列。但是如果正規表示式含有子表示式的話,也會把子表示式的匹配結果返回出來。

  (2) replace()方法:用於字串的替換,並返回替換後的結果。接收兩個引數,第一個引數是匹配的規則,第二引數是用來替換的字串。第一個引數,可以是字串,也可以是正規表示式。

字串就不多說了,這裡只說是正規表示式的情況,replace會根據正規表示式去匹配對應的結果,然後將其替換成第二引數:

// 正規表示式不帶修飾符g
console.log('abc1234'.replace(/[a-z]/, '0'))
// 正規表示式帶修飾符g
console.log('abc1234'.replace(/[a-z]/g, '0'))複製程式碼

【愣錘筆記】一支穿雲箭、正則來相見

輸出結果可以看出,在不加修飾符g的情況下,字串的repalce方法不會全域性檢索替換,而只是替換了第一次。在全域性匹配模式下,replace方法會把所有的匹配結果全域性替換成第二個引數對應的字串。

前文說子表示式的時候說到,正規表示式會把匹配結果存放在類似\1,\2$1,$2的裡面,在這裡便可以用到$1,$2了。用法就是,在repealce()方法的第二個引數字串中,$1,$2……不再是普通的字串,而是有著特殊一樣的識別符號,對應的值就是正規表示式每一個子表示式匹配到的結果:

// 定義一個匹配一個abc字元後面跟著一個數字的正規表示式
// 並將匹配結果替換成該子表示式的結果
console.log('abc1234'.replace(/abc(\d)/g, '$1')) // 1234複製程式碼

該替換方法,最終將abc1替換成了第一個子表示式匹配的結果1,所以最終結果是1234。

repalce()方法的第二次引數,不僅可以是字串,還可以是一個函式,如果是函式則對每一次的匹配結果呼叫該函式。該函式必須返回一個字串,如果沒有reutrn語句,則是跟正常函式一樣預設返回undefined。如果返回的不是字串型別,會將其轉換成字串型別:

console.log('abc1234'.replace(/abc(\d)/g, function(){ 
    return 'hello'
}))
// 列印結果為hello234複製程式碼

從結果可以看出,將匹配到的字串abc1替換成了hello字串。

同時,該函式有多個引數,第一個引數是當前匹配到的結果,第二個引數開始往後是子表示式匹配到的結果,可以有0-n個。再後面一個引數當前匹配結果在原字串中的下標,最後一個引數原字串:

'abc1234'.replace(/abc(\d)(\d)/g, function(a,b,c,d,f){
    console.log(a,b,c,d,f)
})
// 列印結果為 abc12 1 2 0 abc1234複製程式碼

可以看到列印的結果abc12是正則匹配到的結果,1是第一個子表示式(\d)匹配的結果,2是第二個子表示式(\d)匹配到的結果,0是匹配結果字串在原字串中的下標,abc1234是原字串。再看下匹配到多次結果的情況:

'abc1234abc567'.replace(/abc(\d)(\d)/g, function(a,b,c,d,f){ 
    console.log(a,b,c,d,f)
})
// 下面是列印結果
abc12 1 2 0 abc1234abc567
abc56 5 6 7 abc1234abc567複製程式碼

對於有多次匹配結果的情況,會多次呼叫該函式。

  (3) search()方法查詢匹配結果在原字串中的位置。接收一個字串或者正規表示式作為查詢引數。最終返回查詢到的下標,沒有查詢到則返回-1。

// 引數為字串時
console.log('abcd'.search('d')) // 3

// 引數為正規表示式
console.log('abcd'.search(/d/)) // 3複製程式碼

上面演示了簡單的查詢,對於簡單的查詢,完全是可以使用字串的indexOf()方法,查詢結果都是一樣的。

console.log('abcd'.indexOf('d')) // 3複製程式碼

需要注意的地方就是:1.該方法不會進行全域性查詢,即會忽略修飾符g,一旦匹配到結果即返回下標; 2.會忽略正規表示式的lastIndex屬性,即每次查詢都從字串開始位置重新開始。

var str = 'abcddadd'
// 只返回了第一個d字元的洗標,忽略修飾符g
console.log(str.search(/d/g)) // 依舊輸出了3

// 第二次呼叫,依舊輸出3,即忽略了lastIndex屬性
console.log(str.search(/d/g))複製程式碼

  (4) split()方法: 用於字串分割,接收一個用於分割字串的字串或者正規表示式作為引數,第二個引數為可選的指定返回分割後的陣列長度。

// 第一個引數為''時, 將按字元分割字串
// 這在我們需要把字串作為組數處理時非常有用
'asdfg'.split('') // ['a', 's', 'd', 'f', 'g']
// 可以接收第二引數,作為返回後的陣列長度
'asdfg'.split('', 3) // ["a", "s", "d"]
// 以字串s進行跟個
'asdfg'.split('s') // ["a", "dfg"]
// 以正規表示式匹配結果進行分割
'asdfg'.split(/s/) // ["a", "dfg"]
複製程式碼

比如下面,一個簡易版的解析url中鍵值對引數

/*
 * 簡易解析url中鍵值對引數
 * @param url { String } 待解析的url
 * @return { key1: 1, key2: 2} 返回解析後的鍵值對的物件
 */
const parseQueryString = url => {
    if (!url) throw Error('缺少待解析url')
    let result = {}
    const query = url.split('?')[1]
    query && query.split('&').forEach(e => {
        const parts = e.split('=');
        result[parts[0]] = parts[1];
    })
    return result;
}

parseQueryString('www.baidu.com?key1=1&key2=2') // {ke1: 1, key2: 2}複製程式碼

那就再扯一下陣列的join()方法吧,可以理解為字串split()方法的反作用方法。該方法用於將陣列轉換成字串,接收一個引數,作為拼接符,預設是英文逗號:

[1,2,3,4,5].join() // "1,2,3,4,5" // 預設逗號拼接
[1,2,3,4,5].join('a') // "1a2a3a4a5" // 自定義用字元a拼接複製程式碼


【愣錘筆記】一支穿雲箭、正則來相見

相關文章