一次JavaScript正則的詭異經(jīng)歷記錄
事情是這樣的,最近在寫一個(gè)Node功能的時(shí)候,遇到了一個(gè)正則的問題,覺得挺有意思的,就記錄一下經(jīng)歷和最終問題原因,希望也能幫助到同樣遇到的同學(xué)。
背景
我有一個(gè)Node服務(wù),希望對訪問進(jìn)來的請求進(jìn)行標(biāo)記,如果請求進(jìn)來的path是我定義的路由,那么將標(biāo)記一個(gè)REQ,否則標(biāo)記一個(gè)IVL,用于對于整個(gè)服務(wù)的日志記錄進(jìn)行輸出。那么我通過服務(wù)啟動時(shí),根據(jù)定義的路由,生成一個(gè)RouterMap,通過訪問進(jìn)入時(shí),判斷path是否命中RouterMap來判斷是否預(yù)期訪問。
大概的代碼如下:
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;
}?因?yàn)樯婕暗揭恍﹦討B(tài)路由的原因,不能直接通過path進(jìn)行相等判斷,需要對相應(yīng)的路由規(guī)則生成一個(gè)對應(yīng)的正則表達(dá)式,并且在服務(wù)啟動時(shí)生成,保存在內(nèi)存中進(jìn)行復(fù)用。
生成正常代碼如下:
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');
}
問題
然后在進(jìn)行調(diào)試的時(shí)候發(fā)現(xiàn)一個(gè)奇怪的現(xiàn)象,假設(shè)我有一個(gè)路由為GET /cats/find的路由,通過打點(diǎn)發(fā)現(xiàn)對應(yīng)的正則表達(dá)式,/^\/cats\/find$/gi對/cats/find進(jìn)行匹配的時(shí)候,第一次為true,第二次為false,第三次為true,第四次為false,以此類推。
經(jīng)過反復(fù)驗(yàn)證,node代碼并沒有存在問題,正則表達(dá)式也沒有問題,那么我在瀏覽器中嘗試復(fù)現(xiàn)一下,也是得出同樣的問題。至此我很確定,一定是有一些正則相關(guān)的坑是我以前沒有注意到。于是我反查了一下JavaScript的文檔,終于被我找到原因。

原因
通過查找MDN正則相關(guān)的文檔,被查到以下說明
"nolink">當(dāng)設(shè)置全局標(biāo)志的正則使用test()
如果正則表達(dá)式設(shè)置了全局標(biāo)志,test() 的執(zhí)行會改變正則表達(dá)式 lastIndex屬性。連續(xù)的執(zhí)行test()方法,后續(xù)的執(zhí)行將會從 lastIndex 處開始匹配字符串,(exec() 同樣改變正則本身的 lastIndex屬性值).
下面的實(shí)例表現(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
這不就是我遇到的問題嗎?
通過文檔說明得知,當(dāng)我們正則表達(dá)式帶有g(shù)標(biāo)識進(jìn)行全局匹配時(shí),匹配成功后,regex實(shí)例中會有一個(gè)lastIndex屬性去記錄本次命中正則的最后一位的下標(biāo)+1,用于在下一次調(diào)用test的時(shí)候,從lastIndex開始進(jìn)行匹配。 以前我沒有遇到過大概率是因?yàn)橐韵略颍?/p>
每次進(jìn)行正則校驗(yàn)時(shí),都重新生成正則實(shí)例:/^\/cats\/find$/gi.test('/cats/find') 。

但是因?yàn)檫@次我將正則實(shí)例保存,并反復(fù)使用。從而導(dǎo)致問題。
并且通過驗(yàn)證得出,當(dāng)匹配成功后,lastIndex會記錄下一次開始的位置,但是當(dāng)匹配失敗,lastIndex會歸零從頭開始。

至此這一次被坑經(jīng)歷耗時(shí)60分鐘左右,耽誤了吃飯最佳時(shí)間,導(dǎo)致飯?zhí)貌硕伎鞗]有。但是同時(shí)也收獲到JavaScript在正則上一個(gè)容易被忽略的坑。好像也不虧。
總結(jié)
到此這篇關(guān)于一次JavaScript正則的詭異經(jīng)歷記錄的文章就介紹到這了,更多相關(guān)JavaScript正則經(jīng)歷內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
js圖片實(shí)時(shí)加載提供網(wǎng)頁打開速度
沒必要一開始加載就要把全部圖片加載出來,這樣子打開網(wǎng)面的速度得到了很好提高,下面有個(gè)不錯(cuò)的思路,大家可以看看2014-09-09
基于Javascript實(shí)現(xiàn)的不重復(fù)ID的生成器
本文介紹了js生成一個(gè)不重復(fù)的ID的函數(shù)的進(jìn)化之路,具有一定的參考價(jià)值,需要的朋友一起來看下吧2016-12-12
layer.open彈層查看縮略圖的原圖,自適應(yīng)大小的實(shí)例
今天小編就為大家分享一篇layer.open彈層查看縮略圖的原圖,自適應(yīng)大小的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09
JS插件amCharts實(shí)現(xiàn)繪制柱形圖默認(rèn)顯示數(shù)值功能示例
這篇文章主要介紹了JS插件amCharts實(shí)現(xiàn)繪制柱形圖默認(rèn)顯示數(shù)值功能,結(jié)合實(shí)例形式分析了amCharts插件繪制柱形圖并顯示數(shù)值的相關(guān)操作技巧,需要的朋友可以參考下2019-11-11
javascript for-in有序遍歷json數(shù)據(jù)并探討各個(gè)瀏覽器差異
這篇文章主要介紹了javascript for-in有序遍歷json數(shù)據(jù)并探討各個(gè)瀏覽器差異的相關(guān)資料,需要的朋友可以參考下2015-11-11
JavaScript實(shí)現(xiàn)將Excel文件渲染在頁面上
這篇文章主要為大家詳細(xì)介紹了如何使用Html和JavaScript實(shí)現(xiàn)將Excel文件渲染在頁面上,文中的示例代碼講解詳細(xì),有需要的小伙伴可以參考下2024-12-12
window resize和scroll事件的基本優(yōu)化思路
在項(xiàng)目中使用scroll事件去加載數(shù)據(jù),結(jié)果IE下悲劇了。下面為大家介紹下window resize和scroll事件的基本優(yōu)化思路,需要的朋友可以參考下2014-04-04

