零寬:只匹配位置,在匹配過程當中,不佔用字符,因此被稱爲零寬git
先行:正則引擎在掃描字符的時候,從左往右掃描,匹配掃描指針未掃描過的字符,先於指針,故稱先行github
後行:匹配指針已掃描過的字符,後於指針到達該字符,故稱後行,即產生回溯正則表達式
正向:即匹配括號中的表達式api
負向:不匹配括號中的表達式ui
es5 就支持了先行斷言google
es2018 才支持後行斷言url
注意:
.
在正則裏面表明匹配除換行符,回車符等少數空白字符以外的任何字符,匹配其時須要轉義
(?=pattern):某位置後面緊接着的字符序列要匹配 patternes5
例:spa
`sinM.`.match(/sin(?=M\.)/g); // ["sin"]
`M.sin`.match(/sin(?=M\.)/g); // null
複製代碼
第一個 sin 會匹配,由於他後面有 pattern指針
(?!pattern):某位置後面緊接着的字符序列不能匹配 pattern
例:
`M.sin`.match(/sin(?!M\.)/g); // ["sin"]
`sinM.`.match(/sin(?!M\.)/g); // null
複製代碼
第一個 sin 會匹配,由於他後面沒有 pattern
(?<=pattern):某位置前面緊接着的字符序列要匹配 pattern
例:
'sinM.'.match(/(?<=M\.)sin/g); // null
'M.sin'.match(/(?<=M\.)sin/g); // ["sin"]
複製代碼
第二個 sin 會匹配,由於它前面有 pattern
(?<!pattern):某位置前面緊接着的字符序列不能匹配 pattern
例:
'sinM.'.match(/(?<!M\.)sin/g); // ["sin"]
'M.sin'.match(/(?<!M\.)sin/g); // null
複製代碼
第一個 sin 會匹配,由於它前面沒有 pattern
來看個實際的例子,把4+6*sqrt(5)*Math.sqrt(5)
轉換成能夠經過eval
或者new Function()
得到實際結果的字符串
這個可使用負向後行斷言,即替換前面不緊接 Math.的 sqrt 字符串序列
let s = `4+6*sqrt(5)*Math.sqrt(5)`.replace(/(?<!Math\.)sqrt/g, func => `Math.${func}`);
eval(s); // 34
複製代碼
第二個例子: 匹配 url 後面的路徑
'https://www.google.com/v3/api/getUser?user=panghu'.match(/(?<=\.\w*(?=\/)).*/);
複製代碼
第三個例子:替換字符串中 img 標籤的 width 爲 100%
'<img id = "23" style="width:999x;"/><img id = "23" style="width:999x;"/>'.replace(
/(?<=(<img[\s\S]*width:\s*))[^("\/);]*/gm,
'100%'
);
複製代碼
匹配 sin
'M.sin'.match(/(?<=M\.)sin/g); // ["sin"]
`M.sin`.match(/sin(?!M\.)/g); // ["sin"]
複製代碼
這兩種方法均可以實現一樣的效果,但我我的更喜歡使用第一種方法,它的寫法更符合人的直接思惟習慣
先看下面兩行代碼的運行結果
let reg = /js/g;
reg.test('js'); //before: lastIndex:0, after: lastIndex:2
reg.test('js'); //before: lastIndex:2, after: lastIndex:0
reg.test('js'); //before: lastIndex:0, after: lastIndex:2
複製代碼
若是你的答案是三個 true 的話,那就錯了 答案實際上是 true、false、true,這就是所謂的怪異現象
爲何?答: RegExp 對象有個 lastIndex 屬性,它的初始值是 0, 當不使用 g 修飾符修飾時,每次執行 test 方法以後它都會自動置 0 而使用 g 修飾符時,每次執行 test 方法的時候,它都是從索引值爲 lastIndex 的位置開始匹配,lastIndex 爲匹配到的字符序列下一個索引值。只有當匹配失敗之後纔會將 lastIndex 置爲 0
例:上述例子中的第一個 test 方法執行以前,lastIndex 值爲 0,執行以後 lastIndex 值爲 2,因而當第二次執行 test 方法時,從字符串索引值爲 2 處開始匹配,顯然會匹配失敗,因此第三次匹配時又會匹配成功
<div class="root">
這裏能夠涉及到的知識點有:貪婪/非貪婪匹配,模式匹配,回溯及其消除,分組,反向引用
`<div class="root"><span class="root"></span><i class='root'></i></div>`.match(/<[^>]*class="root".*?>/g);
// ["<div class="root">", "<span class="root">"]
複製代碼
模式匹配[^>]
表示匹配除[^]
裏面的全部字符,這裏就是匹配除>
外的全部字符 注意先後都須要非貪婪匹配符號?不然只有前面的,它會貪婪的吃掉 div;只有後面的,它會貪婪的吃掉 span
`<div class="root"><span class="root"></span><i class='root'></i></div>`.match(/<[^>]*class=("root"|'root').*?>/g);
// ["<div class="root">", "<span class="root">", "<i class='root'>"]
複製代碼
這裏若是不使用[^>]
而使用.*
就會出現下面這種匹配結果,不是咱們想要的
["<div class="root">", "<span class="root">", "</span><i class='root'>"]
("root"|'root')
,再消除.*?
回溯`<div class="root"><span class="root"></span><i class='root'></i></div>`.match(/<[^>]*class=("|')root\1[^>]*>/g);
// ["<div class="root">", "<span class="root">", "<i class='root'>"]
複製代碼
\1
表示引用前面的第一個分組結果,即("|')
的匹配結果,這樣就能保證單引號配對單引號,雙引號匹配雙引號
[^>]*
代替.*?
能夠消除使用*?
引起的回溯,由於*
是儘量多的匹配,而?
是儘量少的匹配
回顧開頭,我所說的特殊狀況就是標籤的屬性值不能含有>
,由於爲了消除回溯使用的[^>]
含有字符>,這部分其實可使用其餘正則代替,讓它在消除回溯的狀況下能夠匹配特殊狀況
若是你們對匹配含 class 爲 root 的標籤這部分涉及的知識點感興趣,能夠在底下評論,我到時候再仔細講
若是你喜歡這篇文章的話,麻煩點個⭐原文地址資瓷下
參考:
JavaScript 權威指南(第 6 版)
Javascript 正則表達式迷你書
以上若有錯誤,歡迎指正