JavaScript 對象與繼承教程之內置對象(下)
5、 RegExp類
現在要介紹的內容不完全與RegExp類有關,他幾乎包含了正則表達式的大部分的基本知識點和含義,但是我無意擴展出來說,如果對此感興趣,推薦些教程和好書。以下推薦絕非隨機、隨意。
《精通正則表達式》,該書的作者和譯者都對正則表達式有十分深厚的功底和理解,同時該書也是我目前為止見到的最好的教材,而且不用擔心你的英文不好,該書的中文版翻譯質量絕對一流。不過,這本書正如書名一樣,是給對正則有興趣,且真心的希望有所精通的人看的。
blog.csdn.net/lxcnn。該博客絕對是當今中國數一數二的關于正則方面的精品,博主的造詣絕不含糊,且文筆和內容也十分值得大家一看的。
《正則表達式傻瓜書》,該書的作者即《精通》一書的譯者,有幸翻閱了該書的樣章,書中的內容深淺合宜,語言精煉,加之作者深厚的功底,書的質量不言而喻。雖然該書尚未出版,但是絕對的值得期待。
《正則表達式必知必會》,該書短小易懂,適合不大希望精通于此,僅限于能用即可的朋友閱讀,該書中有許多實例,很多內容大家甚至可以拿來就用,只是受制于其篇幅,該書不大適合進階閱讀。
如果你對以上的珍品不感興趣,那么下面的內容希望你能耐心的看下去,我會盡我所能來讓你有所收獲。
正則表達式通常用于查找、驗證、替換字符串內容。其便捷和高效致使它漸漸的為大家所接收和喜愛,我想很多看到這里的朋友可能會有這樣的感受:“每次用到的時候我都會翻一遍正則的簡明教程或手冊,但是總是記不住,而且當有一天翻自己所寫的正則時,會看不懂。”。那么沒關系,記不住是因為你沒有深入的去了解和思考它,我不會窮舉每一個元字符和語法結構,盡量讓它變得有那么一點意思。
案例一、驗證一個字符串由8到16位數字、英文字母、下劃線組成。
這個驗證是最基本且最常用的,驗證的首要條件是限制死首和尾,即正則必須匹配整個字符串,從起始字符一直到結尾字符。先看正:
/ ^ [ \d a-zA-Z_] {8,16} $ /
這個正則中沒有任何的空白,兩個/之間的內容就是正則表達式,在JavaScript中我們有兩個方式創建正則表達式對象,上面的是字面量對象,另外一個是構造函數方法:
var reg = new RegExp("^[\\da-zA-Z_]{8,16}$","mode");
關于模式,有g、i、m三個,i表示忽略大小寫,g和m會在后面深入解析,因為簡單的說個一兩句不會太容易理解。
兩者效果幾乎等效,而字面量方式簡潔的許多,細心的你一定看到后一種方法中\d前多了一個\。原因是字符串中\x會被當作轉義字符進行轉義。
接下來看藍色部分,^和$符號分別代表字符串的開始和字符串的結尾,但是在m模式的時候他們的含義會有不同,在m模式下,他們分別代表行首和行尾。
[]之間的是字符集合,這相當于字符的switch,[^...]則是匹配非集合。這個很好理解,在你看過的任何一篇教程中都不會漏下它,不過你一定想知道匹配不等于某個字符串的字符串該怎么辦,答案是否定環視(斷言、預查,以下統一稱為環視),一會在介紹環視時,我們再來介紹。
{8,16}表示一個范圍量詞,他表示匹配之前的分組或字符重復多少次。與之相對應的還有+,*,?,{m,},{,n}等。
案例二、驗證一個有數字、字母組成的8-12位字符串。必須包含至少一個數字和字母。
這個與案例一有點區別,那就是最后一句的限制,所以我們需要引入一個判斷邏輯,那就是環視,先引入環視的概念和解釋吧^_^。
環視從方向來說分為逆序和順序,從邏輯來說分為肯定和否定。不過JavaScript正則表達式不支持逆序的環視,只支持順序肯定環視、順序否定環視。環視有個特點就是不占據所匹配字符,所以他會預先進行一次匹配,然后匹配完了之后會回溯會環視匹配之前的位置。讓我們看剛才的案例吧。首先進行2次否定環視,分別排除掉純粹由數字或由字母組成的字符串。然后再把案例一的正則表達式放進來就可以了。如:
/^(?!\d+$)(?!\D+$)[a-zA-Z\d]{8,12}$/
假設字符串為abcdefg123。那么匹配的過程如下。首先^匹配字符串開始,接著是第一個否定環視,(?!\d+$)他從'a'開始匹配,\d+即不能匹配,匹配失敗,否定環視成功,接下來匹配(?!\D+$),\D表示非數字,那么從a-g都可以匹配,接下來是1,\D+匹配完成,如果沒有$那么這個匹配其實也算成功,但是\D+匹配完成之后有一個$,那么$匹配1失敗。否定環視成功,此時字符串位置回到a之前。開始后面的匹配。[a-zA-Z\d]{8,12}匹配abcdefg123成功,具體過程忽略,然后把句柄交給$,匹配成功。此時整個匹配成功。因為篇幅和表述能力問題,關于環視的解釋不夠清晰,如果至此你依然沒有弄清。那么請訪問過客的相關博客,真的很棒( blog.csdn.net/lxcnn )。
好吧,我們現在倒回來看一些日常應用中的一些誤解導致的錯誤應用。
1、 包含字母數字的字符串判斷使用\w是不對的。因為\w<==>[a-zA-Z0-9_],所以使用時請注意
2、 JavaScript中不支持逆序環視。無論是肯定還是否定,因此如果其他語言的程序員在使用js正則的時候千萬要注意
3、 .元字符在字符組中是普通字符,而不解釋為任意字符的元字符,同時.元字符不匹配\n(換行符),JavaScript中沒有單行模式以使.匹配\n。
4、 在使用test、exec時一定要弄清楚g模式之后再使用。因為一旦使用了g模式,那么會保存全局的lastIndex,在你下一次進行匹配時是從lastIndex開始,而非字符串首。(感謝過客之前對此的解釋和指導。有興趣的可以看這個帖子,看過客的解釋 http://topic.csdn.net/u/20110117/18/e2423564-856b-4f46-8879-0c382a3e1c7a.html?93335 )。在這里我把其中的例子發出來,讓大家有個了解,請看:
var reg = /^\d+$/g ;
for ( var i = 0 ; i < arr . length ; i ++){
alert ( reg . test ( arr [ i ]));
}
var arr = [ 1 , 2 , 3 , 4 , 5 ];
var reg = /^\d+$/g ;
for ( var i = 0 ; i < arr . length ; i ++){
alert ( reg . test ( arr [ i ]));
}
5、 js正則不支持命名分組,所以后向引用的時候要格外注意分組的序號。
6、 js正則不支持固話分組和占有優先量詞。所以如果可以的話,[^xx]+取代.+?來使用,一方面可以提高效率,另一方面可以減少錯誤的匹配結果。
7、 正則效率上,new RegExp()與字面量方式創建沒有區別,而使用變量保存一個正則對象和循環中使用同一個正則表達式字面量效率上也沒區別(至少在大部分的瀏覽器上是這樣的)。
8、 在使用new RegExp()創建正則的時候,切記轉義問題。new RegExp("\\d")才等價于/\d/,使用new RegExp("\d")就會得到錯誤的正則。
9、 回溯和環視是2個非常重要的概念,同時也要弄清楚位置和字符的區別。時間和能力的原因,推薦移步過客的博客( http://blog.csdn.net/lxcnn/archive/2009/06/28/4304754.aspx 和 http://blog.csdn.net/lxcnn/archive/2009/06/28/4304651.aspx )
關于正則目前僅僅只提這些,因為后面準備了一個實戰博客,那個時候會詳細介紹幾個實用的實例以及推導過程。下面看看在介紹字符串的時候落下的幾個方法。replace/match/search,OK,我們來一個一個看吧
replace方法是提供強大的替換功能,他不僅能夠替換簡單的文本。還能使用一個替換函數來進行更復雜的替換,請看下面的代碼:
var reg = /dream/g ;
var i = 0 ;
alert ( str . replace ( reg , function ( m ){
return m + ( i ++). toString ();
}));
var str = "dream on, dream on, dream on,dream yourself a dream come true.dream on, dream on, dream on," ;
var reg = /dream/g ;
var i = 0 ;
alert ( str . replace ( reg , function ( m ){
return m + ( i ++). toString ();
}));
該方法通過一個匿名方法,將查找到的dream單詞進行替換,該替換會給后面的dream單詞添加一個序列。你可能會想到下面的方法:
var reg = /(dream)/g ;
var i = 0 ;
alert ( str . replace ( reg , "$1" + i ++));
var str = "dream on, dream on, dream on,dream yourself a dream come true.dream on, dream on, dream on," ;
var reg = /(dream)/g ;
var i = 0 ;
alert ( str . replace ( reg , "$1" + i ++));
可惜很遺憾你運行一下就會發現每次都是0。這點在任何可以使用委托方法(匿名方法、函數指針==)進行替換的語言中都是一樣的,這個和替換機制是有關的。
replace的時候非正則替換有個缺陷,就是只會替換第一個。相當于沒有加上g模式的正則。所以使用的時候一定要注意,當然在替換的時候不要忘了g模式喲。
match則是查找匹配的方法,他同樣支持正則表達式作為查找模式,不過有一點需要注意,如果使用全局檢索(g模式)的時候,返回的是一個數組,該數組不包括分組信息。而如果不使用全局檢索(g模式)的時候,返回的是第一個匹配的文本,以及各分組的匹配內容。比如下面的代碼:
var reg = /(dream)[^,]+/ ;
alert ( str . match ( reg )); //返回數組,數組內容為 dream on,dream2 個元素。
reg = /(dream)[^,]+/g ;
alert ( str . match ( reg )); //返回數組,數組內容為 dream on,dream on,dream on,dream yourself a dream come true.dream on, dream on,dream on 幾個元素。
var str = "dream on, dream on, dream on,dream yourself a dream come true.dream on, dream on, dream on," ;
var reg = /(dream)[^,]+/ ;
alert ( str . match ( reg )); //返回數組,數組內容為 dream on,dream2 個元素。
reg = /(dream)[^,]+/g ;
alert ( str . match ( reg )); //返回數組,數組內容為 dream on,dream on,dream on,dream yourself a dream come true.dream on, dream on,dream on 幾個元素。
最后一個是search,他接收一個正則對象,他返回search到的第一個匹配文本的起始位置,該方法不受g模式的影響。
reg = /dream/g ;
alert ( str . search ( reg ));
alert ( str . search ( reg ));
alert ( str . search ( reg ));
var str = "dream on, dream on, dream on,dream yourself a dream come true.dream on, dream on, dream on," ;
reg = /dream/g ;
alert ( str . search ( reg ));
alert ( str . search ( reg ));
alert ( str . search ( reg ));
該方法三次alert出的都是0。其實還有一個split方法也支持正則。不過它很簡單,在這里不再提了。
RegExp有以下的屬性需要記住,lastIndex是最后一次匹配的位置(還記得g模式么),source是正則表達式的源文本,而$1、$+、$$等意味著什么請可查看下手冊,這里就不一一列舉了。
6、 Global對象
這里沒有用類這個名詞,是因為連這個關鍵字實際上都不存在,在JavaScript中所有的函數都是對象的方法,所以你看到的所有內置的“函數”其實都是全局對象的方法。
編碼、解碼方法,對uri進行編碼是為了使防亂碼的一個有效措施,很多后臺語言也都有相對應的解碼、編碼方法。 encodeURI/encodeURIComponent/decodeURI/decodeURIComponent。
如果不能區分其中的區別,那么用后者代替前者吧。
escape/unescape基本上不推薦使用,用上面的方法來取代吧。
類型轉換的方法也是比較常用的方法。比如Number、String方法,但是看似簡單的2個方法還有真點需要補充的。Number方法不能取代parseInt和parseFloat方法,因為Number方法只能把真正的數字轉換為數字(該方法用來驗證一個輸入是否是數字很好吧)。先看這4句代碼。
alert ( Number ( '011' )); //11
alert ( String ( 0X11 )); //17
alert ( String ( 011 )); //9
alert ( Number ( '01x' )); //NaN
alert ( Number ( '011' )); //11
alert ( String ( 0X11 )); //17
alert ( String ( 011 )); //9
其實關于這點我在之前的博客提到過,現在再拿出來解釋下。第一個如果用parseInt會得到1,而用Number強轉將返回NaN。千萬要使用isNaN來判斷是否成功轉換,而不是Number('01x').constructor == Number。因為NaN是個數字類型,但是他的意思是Not a Number。第二個返回的是11,看上去很正常啊?可是011往往可能意味著是8進制的數哦。所以字符串形式的011和數字型的011可不是一個意思哦。所以在使用數字字面量的時候注意前置0的使用。第三行返回的是17,為什么?剛才提到過0X11是16進制的數字,那么他翻譯為17就意味著轉換為string類型前會強轉為10進制數,第三行同理。那么第三行想輸出11怎么辦呢,使用它:(0x11).toString('16'),8進制也一樣哦。所以轉換的時候要了解清楚先。
最后一個要說的是最有爭議的eval方法。他的功能十分強大,但是缺點也異常明顯,代碼不易讀、性能低下、安全性降低。所以一般情況下可以用setTimeout、setInterval、數組等方法來替代。注意在處理json的時候要把json包括在一個括號里哦:eval('(' + jsondata + ')');
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/cj205/archive/2011/01/24/6161968.aspx