一次JavaScript正則的詭異經歷記錄
事情是這樣的,最近在寫一個Node功能的時候,遇到了一個正則的問題,覺得挺有意思的,就記錄一下經歷和最終問題原因,希望也能幫助到同樣遇到的同學。
背景
我有一個Node服務,希望對訪問進來的請求進行標記,如果請求進來的path是我定義的路由,那么將標記一個REQ,否則標記一個IVL,用于對于整個服務的日志記錄進行輸出。那么我通過服務啟動時,根據(jù)定義的路由,生成一個RouterMap,通過訪問進入時,判斷path是否命中RouterMap來判斷是否預期訪問。
大概的代碼如下:
export function getSourceMak(
? routerMap: AppRouterMap[],
? req: http.IncomingMessage,
): SourceMark.REQ | SourceMark.IVL | SourceMark.TST {
? const { url, method, headers } = req;
? const pathname = url.split('?')[0];
? const userAgent = headers['user-agent'];
? // 安全掃描
? if (userAgent?.includes('TST(Tencent') && userAgent.includes('Team)')) {
? ? return SourceMark.TST;
? }
? for (const item of routerMap) {
? ? const { reg } = item;
? ? if (reg.test(pathname) && item.method === method.toLocaleLowerCase()) {
? ? ? return SourceMark.REQ;
? ? }
? }
? return SourceMark.IVL;
}?因為涉及到一些動態(tài)路由的原因,不能直接通過path進行相等判斷,需要對相應的路由規(guī)則生成一個對應的正則表達式,并且在服務啟動時生成,保存在內存中進行復用。
生成正常代碼如下:
export function createRouterRegexp(url) {
const urlBlock = url.split('/');
const regBlock = urlBlock.map((block) => {
if (block[0] === ':') {
return '((?!/).)*';
}
return block;
});
return new RegExp(`^${regBlock.join('/')}$`, 'ig');
}
問題
然后在進行調試的時候發(fā)現(xiàn)一個奇怪的現(xiàn)象,假設我有一個路由為GET /cats/find的路由,通過打點發(fā)現(xiàn)對應的正則表達式,/^\/cats\/find$/gi對/cats/find進行匹配的時候,第一次為true,第二次為false,第三次為true,第四次為false,以此類推。
經過反復驗證,node代碼并沒有存在問題,正則表達式也沒有問題,那么我在瀏覽器中嘗試復現(xiàn)一下,也是得出同樣的問題。至此我很確定,一定是有一些正則相關的坑是我以前沒有注意到。于是我反查了一下JavaScript的文檔,終于被我找到原因。

原因
通過查找MDN正則相關的文檔,被查到以下說明
"nolink">當設置全局標志的正則使用test()
如果正則表達式設置了全局標志,test() 的執(zhí)行會改變正則表達式 lastIndex屬性。連續(xù)的執(zhí)行test()方法,后續(xù)的執(zhí)行將會從 lastIndex 處開始匹配字符串,(exec() 同樣改變正則本身的 lastIndex屬性值).
下面的實例表現(xiàn)了這種行為:
var regex = /foo/g;
// regex.lastIndex is at 0
regex.test('foo'); // true
// regex.lastIndex is now at 3
regex.test('foo'); // false
RegExp.prototype.test() - JavaScript | MDN
這不就是我遇到的問題嗎?
通過文檔說明得知,當我們正則表達式帶有g標識進行全局匹配時,匹配成功后,regex實例中會有一個lastIndex屬性去記錄本次命中正則的最后一位的下標+1,用于在下一次調用test的時候,從lastIndex開始進行匹配。 以前我沒有遇到過大概率是因為以下原因:
每次進行正則校驗時,都重新生成正則實例:/^\/cats\/find$/gi.test('/cats/find') 。

但是因為這次我將正則實例保存,并反復使用。從而導致問題。
并且通過驗證得出,當匹配成功后,lastIndex會記錄下一次開始的位置,但是當匹配失敗,lastIndex會歸零從頭開始。

至此這一次被坑經歷耗時60分鐘左右,耽誤了吃飯最佳時間,導致飯?zhí)貌硕伎鞗]有。但是同時也收獲到JavaScript在正則上一個容易被忽略的坑。好像也不虧。
總結
到此這篇關于一次JavaScript正則的詭異經歷記錄的文章就介紹到這了,更多相關JavaScript正則經歷內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
基于Javascript實現(xiàn)的不重復ID的生成器
本文介紹了js生成一個不重復的ID的函數(shù)的進化之路,具有一定的參考價值,需要的朋友一起來看下吧2016-12-12
JS插件amCharts實現(xiàn)繪制柱形圖默認顯示數(shù)值功能示例
這篇文章主要介紹了JS插件amCharts實現(xiàn)繪制柱形圖默認顯示數(shù)值功能,結合實例形式分析了amCharts插件繪制柱形圖并顯示數(shù)值的相關操作技巧,需要的朋友可以參考下2019-11-11
javascript for-in有序遍歷json數(shù)據(jù)并探討各個瀏覽器差異
這篇文章主要介紹了javascript for-in有序遍歷json數(shù)據(jù)并探討各個瀏覽器差異的相關資料,需要的朋友可以參考下2015-11-11
JavaScript實現(xiàn)將Excel文件渲染在頁面上
這篇文章主要為大家詳細介紹了如何使用Html和JavaScript實現(xiàn)將Excel文件渲染在頁面上,文中的示例代碼講解詳細,有需要的小伙伴可以參考下2024-12-12
window resize和scroll事件的基本優(yōu)化思路
在項目中使用scroll事件去加載數(shù)據(jù),結果IE下悲劇了。下面為大家介紹下window resize和scroll事件的基本優(yōu)化思路,需要的朋友可以參考下2014-04-04

