在 ES5 中git
RegExp 構造函數的參數有兩種狀況github
var regex = new RegExp('xyz', 'i'); // 等價於 var regex = new RegExp(/xyz/i); // 不能再有 第二參數修飾符,不然會報錯 // 等價於 var regex = /xyz/i;
lastIndex
屬性 指定每次搜索的開始位置,g
修飾符從這個位置開始向後搜索,直到發現匹配爲止const REGEX = /a/g; REGEX.lastIndex = 2; // 指定從2號位置(y)開始匹配 const match = REGEX.exec('xaya'); // 匹配成功 match.index; // 3 // 在3號位置匹配成功 REGEX.lastIndex; // 4 // 下一次匹配從4號位開始 REGEX.exec('xaya'); // null // 4號位開始匹配失敗
在 ES6 中正則表達式
若是 RegExp
構造函數第一個參數是一個正則對象,那麼仍是可使用第二個參數指定修飾符。數組
並且,返回的正則表達式會忽略原有的正則表達式的修飾符,只使用新指定的修飾符ide
new RegExp(/abc/ig, 'i').flags // "i" 而不是 'ig'
字符串對象共有 4 個方法,可使用正則表達式:match()、replace()、search() 和 split()函數
ES6 將這 4 個方法,在語言內部所有調用 RegExp
的實例方法,從而作到全部與正則相關的方法,全都定義在 RegExp
對象上編碼
String.prototype.match 調用 RegExp.prototype[Symbol.match]
String.prototype.replace 調用 RegExp.prototype[Symbol.replace]
String.prototype.search 調用 RegExp.prototype[Symbol.search]
String.prototype.split 調用 RegExp.prototype[Symbol.split]
u
修飾符,含義爲「Unicode 模式」,用來正確處理大於 \uFFFF
的 Unicode 字符。spa
也就是說,會正確處理四個字節的 UTF-16 編碼prototype
/^\uD83D/u.test('\uD83D\uDC2A'); // false /^\uD83D/.test('\uD83D\uDC2A'); // true
/\u{61}/.test('a'); // false /\u{61}/u.test('a'); // true /\u{20BB7}/u.test('𠮷'); // true
\S
是預約義模式,匹配全部非空白字符。只有加了u
修飾符,它才能正確匹配碼點大於0xFFFF
的 Unicode 字符/^\S$/.test('𠮷'); // false /^\S$/u.test('𠮷'); // true
利用這個特性,封裝一個絕對正確地返回字符串長度的函數設計
function strLength(str) { var result = str.match(/[\s\S]/gu); return result ? result.length : 0; }; var s = '𠮷𠮷'; s.length // 4 codePointLength(s) // 2
unicode
屬性表示是否設置了u
修飾符
RegExp.prototype.unicode
const r1 = /hello/; const r2 = /hello/u; r1.unicode; // false r2.unicode; // true
做用與 g 修飾符相似,也是全局匹配,後一次匹配都從上一次匹配成功的下一個位置開始。
不一樣之處在於,g 修飾符 只要 剩餘位置中存在匹配 就可,
而 y 修飾符 確保匹配必須從剩餘的第一個位置開始,這也就是「粘連」的涵義
const REGEX = /a/y; REGEX.lastIndex = 2; // 指定從2號位置開始匹配 REGEX.exec('xaya'); // null 不是粘連,匹配失敗 REGEX.lastIndex = 3; // 指定從3號位置開始匹配 const match = REGEX.exec('xaya'); // 3號位置是粘連, match.index; // 3 匹配成功 REGEX.lastIndex; // 4
y
修飾符的設計本意,就是讓頭部匹配的標誌^
在全局匹配中都有效/b/y.exec('aba'); // null 因爲不能保證頭部匹配 b,因此返回null const REGEX = /a/gy; 'aaxa'.replace(REGEX, '-'); // '--xa' 最後一個由於不是出如今下一次匹配的頭部,因此不會被替換a
y
修飾符對 match
方法,只能返回第一個匹配,必須與 g
修飾符聯用,才能返回全部匹配'a1a2a3'.match(/a\d/y); // ["a1"] 'a1a2a3'.match(/a\d/gy); // ["a1", "a2", "a3"]
從字符串提取 token(詞元),y
修飾符確保了匹配之間不會有漏掉的字符
function tokenize(TOKEN_REGEX, str) { let result = []; let match; while (match = TOKEN_REGEX.exec(str)) { result.push(match[1]); }; return result; }; const TOKEN_Y = /\s*(\+|[0-9]+)\s*/y; const TOKEN_G = /\s*(\+|[0-9]+)\s*/g; tokenize(TOKEN_Y, '3 + 4'); // [ '3', '+', '4' ] tokenize(TOKEN_G, '3 + 4'); // [ '3', '+', '4' ] // g修飾符會忽略非法字符,而 y 修飾符不會 tokenize(TOKEN_Y, '3x + 4'); // [ '3', '+', '4' ] tokenize(TOKEN_G, '3x + 4'); // ['3'] 不是咱們想要的現象
RegExp.prototype.sticky
是否設置了 y
修飾符
var r = /hello\d/y; r.sticky; // true var r = /hello\d/y; r.sticky; // true
flags
屬性,會返回正則表達式的修飾符// ES5 的 source 屬性 // 返回正則表達式的正文 /abc/ig.source // "abc" // ES6 的 flags 屬性 // 返回正則表達式的修飾符 /abc/ig.flags // 'gi'
dotAll 模式,即 點(dot)表明一切字符。
s修飾符,使得 . 能夠匹配任意單個字符
RegExp.prototype.dotAll
返回一個布爾值,表示該正則表達式是否處在dotAll
模式
const re = /foo.bar/s; // 另外一種寫法 // const re = new RegExp('foo.bar', 's'); re.test('foo\nbar'); // true 正確地將 \n 識別了出來 re.dotAll; // true re.flags; // 's'
JavaScript 語言的正則表達式 斷言
以前只支持
x
只有在 y
前面才匹配,必須寫成/x(?=y)/
好比,只匹配百分號以前的數字,要寫成/\d+(?=%)/
x 只有不在 y 前面才匹配,必須寫成/x(?!y)/
好比,只匹配不在百分號以前的數字,要寫成/\d+(?!%)/
/\d+(?=%)/.exec('100% of US presidents have been male'); // ["100"] /\d+(?!%)/.exec('that’s all 44 of them'); // ["44"]
ES6 引入
x
只有在 y
後面才匹配,必須寫成/(?<=y)x/
比如,只匹配美圓符號以後的數字,要寫成/(?<=\$)\d+/
x
只有不在 y
後面才匹配,必須寫成/(?<!y)x/
好比,只匹配不在美圓符號後面的數字,要寫成/(?<!\$)\d+/
/(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill'); // ["100"] /(?<!\$)\d+/.exec('it’s is worth about €90'); // ["90"]
\p{UnicodePropertyName=UnicodePropertyValue}
一種新的類的寫法 \p{...} 和 \P{...},容許正則表達式匹配符合 Unicode 某種屬性的全部字符
這兩種類只對 Unicode 有效,因此使用的時候必定要加上u修飾符。
若是不加u修飾符,正則表達式使用 \p 和 \P 會報錯,ECMAScript 預留了這兩個類
\p{UnicodePropertyName}
\p{UnicodePropertyValue}
\P{…} 是 \p{…} 的反向匹配,即匹配不知足條件的字符
const regex = /^\p{Decimal_Number}+$/u; regex.test('𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼'); // true
\p{Number}
甚至能匹配羅馬數字// 匹配全部數字 const regex = /^\p{Number}+$/u; regex.test('²³¹¼½¾'); // true regex.test('㉛㉜㉝'); // true regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ'); // true
// 匹配全部空格 \p{White_Space} // 匹配各類文字的全部字母,等同於 Unicode 版的 \w [\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}] // 匹配各類文字的全部非字母的字符,等同於 Unicode 版的 \W [^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}] // 匹配 Emoji /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu // 匹配全部的箭頭字符 const regexArrows = /^\p{Block=Arrows}+$/u; regexArrows.test('←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩') // true
具名組匹配(Named Capture Groups)
容許爲每個組匹配指定一個名字,/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
既便於閱讀代碼,又便於引用
const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; const matchObj = RE_DATE.exec('1999-12-31'); const year = matchObj.groups.year; // 1999 const month = matchObj.groups.month; // 12 const day = matchObj.groups.day; // 31
同時,數字序號(matchObj[1]
)依然有效
若是具名組沒有匹配,那麼對應的 groups 對象屬性會是 undefined
具名組 as 沒有找到匹配,那麼 matchObj.groups.as 屬性值就是 undefined,而且 as 這個鍵名在 groups 是始終存在的
const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/; RE_TWICE.test('abc!abc'); // true RE_TWICE.test('abc!ab'); // false
\1
)依然有效const RE_TWICE = /^(?<word>[a-z]+)!\1$/; RE_TWICE.test('abc!abc'); // true RE_TWICE.test('abc!ab'); // false
const RE_TWICE = /^(?<word>[a-z]+)!\k<word>!\1$/; RE_TWICE.test('abc!abc!abc'); // true RE_TWICE.test('abc!abc!ab'); // false
String.prototype.matchAll()
能夠一次性取出全部匹配。
不過,它返回的是一個遍歷器(Iterator),而不是數組
相對於返回數組,返回遍歷器的好處在於,若是匹配結果是一個很大的數組,那麼遍歷器比較節省資源
...
運算符 和 Array.from()
方法就能夠了// 轉爲數組方法一 [...string.matchAll(regex)] // 轉爲數組方法二 Array.from(string.matchAll(regex));