多年前我第一次入職騰訊的時候,DC 從杭州給我寄來了一本他剛翻譯出爐的《高性能 JavaScript》。那段時間爲了幫忙校對,我仔細閱讀了書中的每個段落,結果積累了很多 JavaScript 基礎知識。如今還依稀記得書中提到的幾個知識點: IE7 瀏覽器在大字符串處理時的極致性能優化;位運算符用於 config 配置的各類 trick;以及今天想聊的 RegExp 構造器的第一個參數設計問題。php
上週接到一個需求,根據頁面 url 來決定是否出現一個彈窗提示。爲了方便管理這個特性,我將 url 列表配置在了後臺,前端經過接口取得列表再進行校驗。html
其中有一條規則是「全部機構首頁須要彈窗」,由於機構會有本身的獨立二級域名,因此這裏必需要用到location.host 對應的正則表達式 \w+\.ke\.qq\.com
前端
new RegExp(/\w+\.ke\.qq\.com/).test('ktmaster.ke.qq.com') // 返回 true // 因爲正則表達式字符串是 cgi 接口中返回的,因此第一個參數只能用 string 類型 // 而 RegExp 構造器使用 string 參數時,其中的 \w、\ 等特殊含義字符是須要使用反斜槓再作一層轉義,這樣同時致使正則語義變得很不清晰 new RegExp('\w+\.ke\.qq\.com').test('ktmaster.ke.qq.com') // 返回 false new RegExp('\\w+\\.ke\\.qq\\.com').test('ktmaster.ke.qq.com') // 返回 true
然而,需求真正落地實現後發現:RegExp 構造器 string 參數須要轉義的知識點,其實基本用不到。git
前端 AJAX 請求取到的接口數據必定是 string 類型的,這種未經過字符串字面量形式賦值給變量時是無需轉義的。以 fetchAPI 爲例:github
// 1. 其中 data 接口返回的內容是 \w+\.ke\.qq\.com fetch('/data') .then(res => res.text()) .then(resText => { console.log(new RegExp(resText)) // 正確實例化了 /\w+\.ke\.qq\.com/ }) // 2. 字面量形式定義的字符串不轉義,會與指望不符 const regText = '\w+\.ke\.qq\.com' // 字符串定義時 \ 會與後面一個字符合並解析掉 console.log(regText === 'w+.ke.qq.com') // 返回 true console.log(new RegExp(regText)) // 返回的是 /w+.ke.qq.com/
如今大部分的接口數據會使用 JSON string,接口返回後經過 JSON.parse 成 JavaScript Object ,再經過 key 來取值。而對於 JSON 數據來講,後端 JSON.stringify 時,\ 字符是必定會通過一層轉義的(這樣才符合 JSON 規範)正則表達式
<?php $regText = '\w+\.ke\.qq\.com'; // 注意 PHP 中單引號內的字符串不會通過解析 echo json_encode(array('pattern' => $regText)); // 返回的是 {"pattern":"\\w+\\.ke\\.qq\\.com"}
因此接口場景下,一樣不存在 RegExp 構造器的 string 參數轉義問題。json
假設頁面中存在輸入框 <input id="test">
,在輸入框中輸入字符 \w+\.ke\.qq\.com
,則經過 JS 獲取到的值能夠直接傳入 RegExp 構造器,一樣無需考慮轉義問題。後端
const regText = document.getElementById('test').value new RegExp(regText) // 返回 /\w+\.ke\.qq\.com/
由於表單項中的字符串也是直接賦值,而非經過引號字面量的字符串定義方式賦值。瀏覽器
另一種可能用到 RegExp string 參數的場景是:基於 JS 邏輯,動態建立正則表達式。例如正則表達式 /\w{3}/
中的數字 3,是經過某個變量來傳遞的。那麼在寫正則時須要寫成:性能優化
let n = 3 new RegExp('\\w{' + n + '}') // 這裏的 \w 爲特殊字符,須要通過 \ 轉義
Python 語言中是經過 raw string 修飾符來解決字符串轉義問題,在字符串前加上 r 標記,表示這個字符串的內容不通過解析。即 print r'\n' == '\\n'
返回 True。
爲了解決模板字符串的解析和轉義問題,ES6 模板字面量中引入了反引號(`)和 tag function(知名「CSS in JS」 庫 styled-components 中大量使用了這種語法)。這裏的場景就能夠寫成十分相似 Python 的風格,當須要轉義的內容比較多時,能保持較好的正則表達式語義:
const r = String.raw let n = 3 new RegExp(r`\w{${n}}`)
不過這種使用場景十分罕見,我至今尚未遇到過。
回過頭來看,JS 正則表達式構造器的參數設計問題,其實不是 RegExp 引發的,而是 JavaScript String 的設計缺陷:單引號和雙引號非但沒有參考 PHP/Shell 之類的設計,反而給前端社區留下「應該使用單引號仍是雙引號」的代碼風格爭論。反觀 Golang,在這塊的約束就作得很是好。