本文發佈在個人博客一道小小的題目引起對javascript支持正則表達式相關方法的探討
許可協議: 署名-非商業性使用-禁止演繹 4.0 國際 轉載請保留原文連接及做者。javascript
之前對於正則是很是害怕的,由於看不懂和學不會。但最近項目中頻繁的使用到了正則,所以強迫本身去學習瞭解,慢慢的體會到了他的魅力與強大。固然學習正則初入門的時候有些枯燥難懂,但越學越以爲輕鬆。本文不許備說關於正則自己的事兒,而是說一說關於javascript中關於正則的幾個方法中被不少人忽略的地方。java
說到正則,不少人都是從抄到改到本身寫,這個過程可能有時候很漫長。如一些工具能幫助你快速分析和學習正則,那麼學習的過程你確定要輕鬆得多。下面我推薦兩個我常用的正則在線可視化工具,正則可視化工具圖解符合鐵路圖規律(其實不明白什麼是鐵路同樣很容易看懂,只是一些細微的地方和咱們的常規思惟有點差異)。git
這道題目是在羣裏平常閒聊時,公司同事拋出來的,具體是出自哪裏本人沒去考察。先先說說題目:github
寫一個方法把一個數字末尾的連續0變成9,如1230000變成1239999正則表達式
一道很簡單的題目,直接正則就能搞定,也許你會寫:segmentfault
function zoreToNine(num){ return (num + '').replace(/0/g,9); } //或者 function zoreToNine(num){ return (num + '').replace(/[1-9]0+$/,9); }
這也是此題的陷阱所在,按照上面的方法,1023000就會被轉化成1923999,這樣是不符合要求的,因此改進一下:數組
function zoreToNine(num){ return (num + '').replace(/[1-9]0+$/,function($1){ return $1.replace(/0/g,9); }); } zoreToNine(1223000); //1223999 zoreToNine(1023000); //1023999
關於這個問題的解決方案@微醺歲月同窗提供了一種,位置匹配的方法,簡單了不少,厲害!函數
"12300100000".replace(/0(?=(0+$)|\b)/g,9); //12300199999
固然解決問題的方法不少,不必定非要用正則,還徹底可使用純算術的方法實現,你們有興趣能夠嘗試,閒話少說進入此次的主題:javascript
支持正則表達式相關方法,注意並非正則對象的方法。
上述方法使用了正則,有趣的是在回調函數裏有一個$1,這個$1究竟是什麼?全部的匹配規則匹配後都有$1這個變量麼?...一連串的問題,之前我歷來沒有去追探過,趁着昨個比較空閒,去追探了一番,並在今天整理了一下,寫下此文記錄。工具
javascript
中正則對象有三個方法:test
、exec
和compile
,可是這次的主角並非它們!咱們討論的是可以使用正則表示的相關方法:search
、match
、replace
和split
,注意它們都是String
對象的方法,使用它們必需要是String
類型.學習
replace
是一個用於替換字符串的方法,雖然看似簡單,可是它隱藏的機關也是經常被人忽略。具體分析一下它的特色:
它接收兩個參數 無反作用不影響原始變量 返回被改變的字符串(必定是字符串類型)
定義一些變量,方便全文取用。
let a = '12309800', b = '12309800[object Object]', b = '12309800{}';
在通常狀況,rule參數通常是正則、字符串、數字。
若是是字符串,將會在匹配到第一個符合條件的目標,結束方法;
若是是正則,則按照正則的規則進行匹配
//匹配第一個0替換成5 a.replace(0,5); //'12359800' //匹配全部的0替換成5 a.replace(/0/g,5); //'12359855'
在通常狀況,replacement參數是字符串、數字、者回調。
當參數rule爲正則,而且正則至少包含有一對完整的()
時,若是replacement
包含有$的字符串,那麼對於$n
(n爲大於0的整數,n的長度取決於正則中括號的對數),會被解析成一個變量。可是也僅僅只是做爲一個變量,沒法在字符串中進行計算,此時更相似特別的字符串模板變量。
通常狀況下,$n
中n的長度取決於正則中括號的對數,$1表示第1對括號匹配的結果,$2表示第2對匹配的結果...在正則全部的括號對中,左括號出如今第幾個位置(或者說從左往右),則它就是第幾對括號,以此類推。姑且咱們把這種規則成爲正則匹配分割規則
(ps:這徹底是我本身取的一個名字,方便文章後面使用和記憶)。
a.replace(0,'$0'); //'123$09800' a.replace(/00/g,'$0'); //'123098$0' a.replace(/[1-9]0+$/,'$1'); //'12309$1' a.replace(/([1-9](0+$))/,'$1'); //'12309800',此時$1爲[1-9](0+$)匹配到的內容,$2爲0+$匹配到的內容 a.replace(/([1-9])(0+$)/,'$1'); //'123098',此時$1爲[1-9]匹配到的內容,$2爲0+$匹配到的內容 a.replace(/([1-9])(0+$)/,'$1*$2'); //'123098*00',此處的$1和$2不會安照期待的狀況進行乘法計算,要進行計算能夠用回調
請注意:雖然目前參數replacement中攜帶有$n仍然能正常使用,可是這種方式已經不被規範所推薦,更應該使用回調來完成這個操做。這一點謝謝@lucky4同窗的指出
若是正則中包含有全局匹配標誌(g),那麼每次匹配的都符合上述規則
先看例子:
a.replace(/[1-9]0+$/,function(){ console.log(arguments); //["800",5,"12309800"]、 }); a.replace(/([1-9])0+$/,function(){ console.log(arguments); //["800","8",5,"12309800"] }); a.replace(/([1-9])(0+$)/,function(){ console.log(arguments); //["800","8","00",5,"12309800"] }); a.replace(/(([1-9])(0+$))/,function(){ console.log(arguments); //["800","800","8","00",5,"12309800"] });
回調函數的arguments
數組部分組成:[完整匹配的字符串,$1,$2,...,$n,匹配的開始位置,原始字符串],$1...$n
表示每一個括號對的匹配,規則和前面的相同。
因此有一下規律:
let arr = [...arguments], len = arr.length; (len >= 3) === true; arr[0] = 完整匹配的字符串; arr[len-2] = 匹配的開始位置; arr[len-1] = 原始字符串;
注意:除了匹配的開始位置是Number
類型外,其他的都是String
類型
若是參數類型不是上述兩種狀況,會發生什麼呢?看看下面的例子:
a.replace(0,null); //123null9800 a.replace(0,undefined); //123null9800 a.replace(0,[]); //1239800 a.replace(0,Array); //1230,3,123098009800 b.replace({},5); //123098005 c.replace({},5); //'12309800{}' a.replace(0,{}); //123[object Object]9800 a.replace(0,Object); //12309800
由上面的例子能夠看出,若是非正則也非字符串,則有如下規則:
`null`變量,則會轉換成`'null'`字符串; `undefined`變量,則會轉換成`'undefined'`字符串; `[]`變量,則會調用`join()`方法轉換成字符串,默認以`,`分割,值得注意的是空數組將會被轉換成空字符串(沒有任何字符),一般會被匹配源字符串的開始位置(默認開始位置爲空字符串); 'Array'變量,則會先轉成成一個匹配的數組,形如`[完整匹配的字符串,$1,$2,...,$n,匹配的開始位置,原始字符串]`,而後對它調用`join()`方法轉換成字符串,默認以`,`分割; `{}`變量,則會調用`Object.protype.toString.call()`方法把`{}`轉換成`[object Object]`; `Object`變量,則貌似什麼都沒作
雖然能夠傳入這些非正常參數,但大多數狀況下這些類型的參數對實際是毫無心義的,因此不建議傳入以上類型的參數。同上面的正則匹配分割規則
同樣,爲了方便使用稱呼,姑且我把上面的轉換規則稱爲正則匹配參數轉換規則
match
方法可在字符串內檢索指定的值,或找到一個或多個正則表達式的匹配。
該方法相似indexOf
和lastIndexOf
,可是它返回指定的值,而不是字符串的位置;
參數的傳遞除了常規的正則和字符串之外,其他全部類型的參數都會按照上述的正則匹配參數轉換規則
轉換成字符串形式來匹配。
返回值根據傳入的參數類型和規則的不一樣,返回的內容不一樣,但整體來講,它是返回一個對象,而不是索引,若是沒匹配到任何符合條件的字符串,則返回null
。
若是匹配規則是一個非全局匹配規則,那麼,它此時的返回值是一個僞數組對象(likeArr),形如:[一個展開的匹配到的字符串數組, 匹配到的字符串位置, 原始字符串],它有以下規律:
var likeArr = a.match(regex); likeArr[0] = 匹配到的字符串; likeArr[1...n] = 正則匹配分割規則匹配的字符串; likeArr.index = 匹配到字符串的位置 likeArr.inupt = 原始字符串
看例子:
a.match(/[1-9]0+$/); //[0:'800',index:5,input:'12309800'] a.match(/([1-9])0+$/); //[0:'800',1:'8',index:5,input:'12309800'] a.match(/[1-9](0+$)/); //[0:'800',1:'00',index:5,input:'12309800'] a.match(/([1-9])(0+$)/); //[0:'800',1:'8',2:'00',index:5,input:'12309800']
若是匹配規則是一個全局匹配規則(正在攜帶有g標誌),那麼,它此時的返回值是一個數組對象(arr),形如:[匹配到的字符串數1,匹配到的字符串數2,匹配到的字符串數3];
看例子:
a.match(/[1-9]0/); //[0:'30',index:2,input:'12309800'] a.match(/[1-9]0/g); //[0:'30',1:'80']
search
方法用於檢索字符串中指定的子字符串,或檢索與正則表達式相匹配的子字符串。stringObject
中第一個與rule
相匹配的子串的起始位置。若是沒有找到任何匹配的子串,則返回-1
。
注意:
search
方法不執行全局匹配,它將忽略標誌g
。regexp
的lastIndex
屬性,老是從字符串的開始進行檢索,這意味着它老是返回stringObject
的第一個匹配的位置一樣,search
能夠傳入任何參數類型,它會遵循正則匹配參數轉換規則
進行轉換
這個方法就不用多說,很經常使用的字符串分割方法。
第二個參數的做用就是限制返回值的長度,表示返回值的最大長度
固然,它依然能夠傳入任何參數類型,會遵循正則匹配參數轉換規則
進行轉換
有一段加密的後的密碼,咱們須要分離出字符串'12a344gg333tt445656ffa6778ii99'中的前三組數字,經過某種計算才能得出正確的密碼
'12a344gg333tt445656ffa6778ii99'.split(/[a-zA-Z]+/g,3);//['12','334','333']
寫了這麼多,忽然發現之前僅僅是在用這些方法,瞭解得很不夠深刻。越是學習才發現其中的奧祕!學無止境,與諸君共勉!以上內容若有錯誤之處,但願諸君不吝指出!