一次JavaScript正則的詭異經(jīng)歷記錄
事情是這樣的,最近在寫(xiě)一個(gè)Node功能的時(shí)候,遇到了一個(gè)正則的問(wèn)題,覺(jué)得挺有意思的,就記錄一下經(jīng)歷和最終問(wèn)題原因,希望也能幫助到同樣遇到的同學(xué)。
背景
我有一個(gè)Node服務(wù),希望對(duì)訪問(wèn)進(jìn)來(lái)的請(qǐng)求進(jìn)行標(biāo)記,如果請(qǐng)求進(jìn)來(lái)的path是我定義的路由,那么將標(biāo)記一個(gè)REQ,否則標(biāo)記一個(gè)IVL,用于對(duì)于整個(gè)服務(wù)的日志記錄進(jìn)行輸出。那么我通過(guò)服務(wù)啟動(dòng)時(shí),根據(jù)定義的路由,生成一個(gè)RouterMap,通過(guò)訪問(wèn)進(jìn)入時(shí),判斷path是否命中RouterMap來(lái)判斷是否預(yù)期訪問(wèn)。
大概的代碼如下:
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)樯婕暗揭恍﹦?dòng)態(tài)路由的原因,不能直接通過(guò)path進(jìn)行相等判斷,需要對(duì)相應(yīng)的路由規(guī)則生成一個(gè)對(duì)應(yīng)的正則表達(dá)式,并且在服務(wù)啟動(dòng)時(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'); }
問(wèn)題
然后在進(jìn)行調(diào)試的時(shí)候發(fā)現(xiàn)一個(gè)奇怪的現(xiàn)象,假設(shè)我有一個(gè)路由為GET /cats/find的路由,通過(guò)打點(diǎn)發(fā)現(xiàn)對(duì)應(yīng)的正則表達(dá)式,/^\/cats\/find$/gi對(duì)/cats/find進(jìn)行匹配的時(shí)候,第一次為true,第二次為false,第三次為true,第四次為false,以此類推。
經(jīng)過(guò)反復(fù)驗(yàn)證,node代碼并沒(méi)有存在問(wèn)題,正則表達(dá)式也沒(méi)有問(wèn)題,那么我在瀏覽器中嘗試復(fù)現(xiàn)一下,也是得出同樣的問(wèn)題。至此我很確定,一定是有一些正則相關(guān)的坑是我以前沒(méi)有注意到。于是我反查了一下JavaScript的文檔,終于被我找到原因。
原因
通過(guò)查找MDN正則相關(guān)的文檔,被查到以下說(shuō)明
"nolink">當(dāng)設(shè)置全局標(biāo)志的正則使用test()
如果正則表達(dá)式設(shè)置了全局標(biāo)志,test() 的執(zhí)行會(huì)改變正則表達(dá)式 lastIndex屬性。連續(xù)的執(zhí)行test()方法,后續(xù)的執(zhí)行將會(huì)從 lastIndex 處開(kāi)始匹配字符串,(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
這不就是我遇到的問(wèn)題嗎?
通過(guò)文檔說(shuō)明得知,當(dāng)我們正則表達(dá)式帶有g(shù)標(biāo)識(shí)進(jìn)行全局匹配時(shí),匹配成功后,regex實(shí)例中會(huì)有一個(gè)lastIndex屬性去記錄本次命中正則的最后一位的下標(biāo)+1,用于在下一次調(diào)用test的時(shí)候,從lastIndex開(kāi)始進(jìn)行匹配。 以前我沒(méi)有遇到過(guò)大概率是因?yàn)橐韵略颍?/p>
每次進(jìn)行正則校驗(yàn)時(shí),都重新生成正則實(shí)例:/^\/cats\/find$/gi.test('/cats/find') 。
但是因?yàn)檫@次我將正則實(shí)例保存,并反復(fù)使用。從而導(dǎo)致問(wèn)題。
并且通過(guò)驗(yàn)證得出,當(dāng)匹配成功后,lastIndex會(huì)記錄下一次開(kāi)始的位置,但是當(dāng)匹配失敗,lastIndex會(huì)歸零從頭開(kāi)始。
至此這一次被坑經(jīng)歷耗時(shí)60分鐘左右,耽誤了吃飯最佳時(shí)間,導(dǎo)致飯?zhí)貌硕伎鞗](méi)有。但是同時(shí)也收獲到JavaScript在正則上一個(gè)容易被忽略的坑。好像也不虧。
總結(jié)
到此這篇關(guān)于一次JavaScript正則的詭異經(jīng)歷記錄的文章就介紹到這了,更多相關(guān)JavaScript正則經(jīng)歷內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
原生JS實(shí)現(xiàn)圖片輪播 JS實(shí)現(xiàn)小廣告插件
這篇文章主要為大家詳細(xì)介紹了原生JS實(shí)現(xiàn)圖片輪播、小廣告插件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09Javascript 使用function定義構(gòu)造函數(shù)
Javascript并不像Java、C#等語(yǔ)言那樣支持真正的類。但是在js中可以定義偽類。做到這一點(diǎn)的工具就是構(gòu)造函數(shù)和原型對(duì)象。首先介紹js中的構(gòu)造函數(shù)。2010-02-02Javascript類定義語(yǔ)法,私有成員、受保護(hù)成員、靜態(tài)成員等介紹
JS只是一門(mén)支持面向?qū)ο缶幊痰恼Z(yǔ)言,通過(guò)OO可以讓我們的代碼組織更加人性化??墒桥c傳統(tǒng)基與類的面向?qū)幊陶Z(yǔ)言不同它沒(méi)有類概念并且沒(méi)成員訪問(wèn)修飾符。這多少會(huì)給我們編程工作會(huì)帶來(lái)一些束縛2011-12-12實(shí)例詳解JavaScript中setTimeout函數(shù)的執(zhí)行順序
關(guān)于javascript的運(yùn)行機(jī)制大家都應(yīng)該有所了解了吧,其實(shí)javascript是一個(gè)單線程的機(jī)制,但是因?yàn)殛?duì)列的關(guān)系它的表現(xiàn)會(huì)讓我們感覺(jué)是一個(gè)多線程的錯(cuò)覺(jué)。下面這篇文章通過(guò)實(shí)例主要給大家介紹了關(guān)于JavaScript中setTimeout函數(shù)執(zhí)行順序的相關(guān)資料,需要的朋友可以參考下。2017-07-07數(shù)據(jù)排序誰(shuí)最快(javascript中的Array.prototype.sort PK 快速排序)
今天在51js論壇中看到一個(gè)網(wǎng)友發(fā)布了一個(gè)javasctipt實(shí)現(xiàn)的快速排序的算法,前些日子工作中也涉及到j(luò)avasctipt中數(shù)據(jù)排序的應(yīng)用,當(dāng)時(shí)為了提高排序速度,使用的也是快速排序的算法。2007-01-01layui實(shí)現(xiàn)checkbox的目錄樹(shù)tree的例子
今天小編就為大家分享一篇layui實(shí)現(xiàn)checkbox的目錄樹(shù)tree的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09js校驗(yàn)開(kāi)始時(shí)間和結(jié)束時(shí)間
這篇文章主要為大家詳細(xì)介紹了js校驗(yàn)開(kāi)始結(jié)束時(shí)間,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05