一個(gè)奇怪的 Bug
非常感謝小趙同學(xué)給我反饋的這個(gè) Bug ????
在開始講解前先考考你們 Javascript 基礎(chǔ)摩钙,單看代碼你覺得它會輸出什么內(nèi)容?答案后面揭曉玩焰。
'Hello'.replace('ello', '#$&%')
話說某一天我突然收到一封郵件浅辙,一位同學(xué)跟我說我的站點(diǎn)炸代碼了物咳,嚇得我突然就從床上翻了個(gè)身——感覺充電線有點(diǎn)勒脖子我又翻了回去……
一波詳詢過后我了解到是我的自建博客站點(diǎn)寞宫,也就是現(xiàn)在在寫文章的這個(gè),它在某個(gè)頁面上會顯示一部分代碼在頁面的下方伊群。像這樣:
心情突然就不好了——死去的 Bug 突然又回來攻擊我了拷获。篮撑。
這個(gè)問題其實(shí)之前出現(xiàn)過,但我后來給修好了匆瓜,今天又出現(xiàn)了我第一時(shí)間就抓緊復(fù)現(xiàn)赢笨,但發(fā)現(xiàn)我自己訪問好像沒啥問題,定位到最后發(fā)現(xiàn)是我的服務(wù)端的渲染腳本的問題驮吱。所以如果你是直接進(jìn)入站點(diǎn)再瀏覽文章是不會遇到的茧妒,多半我之前就是這樣才以為它修復(fù)了吧。復(fù)現(xiàn)的方式也很簡單:你得直接由某個(gè)鏈接進(jìn)入到某一篇博文才會觸發(fā)左冬,這就很有意思啦桐筏!基本可以說明它是由于數(shù)據(jù)的原因才會觸發(fā)的。
因?yàn)槲疫@個(gè)服務(wù)端渲染是自己用 Express 寫的拇砰,不是用的現(xiàn)成的框架梅忌,所以有的地方可能是會有問題的也正常狰腌。在一波探索之后定位到一處代碼塊,調(diào)試的結(jié)果非常奇怪牧氮。大致就是:
// 我的頁面模板
const template = fn();
// 渲染出來的頁面樣式
const css = fn();
// 當(dāng)前訪問頁面所用到的 React state
const state = fn();
// 將所有內(nèi)容由不同的錨點(diǎn)定位琼腔,替換到模板中去,每個(gè)錨點(diǎn)有且只有一個(gè)
const page = template
.replace('css anchor', css)
.replace('state anchor', state)
console.log(template.includes('state anchor'))
// true
console.log(page.includes('state anchor'))
// true
console.log(state.includes('state anchor'))
// false
見鬼了踱葛!頁面中的描點(diǎn)在替換后還是存在丹莲?然后我就在后面又給加了另外一條測試:
const page = template
.replace('css anchor', css)
.replace('state anchor', state)
.replace('state anchor', 'f*** me')
console.log(template.includes('state anchor'))
// true
console.log(page.includes('state anchor'))
// false
console.log(state.includes('state anchor'))
// false
就挺奇怪的,我再替換了一次它就舒服了不見了尸诽。甥材。接著我順著描點(diǎn)替換的位置找去,最終發(fā)現(xiàn)它在某段文本中的這個(gè)位置:
" Use <Space-E> to open explorer
" Using Coc-explorer
noremap <space>e :CocCommand explorer<CR>
" Close Coc-explorer if it is the only window
autocmd BufEnter * if (&ft == 'coc-explorer' && winnr("$") == 1) | q | endif
這段是我之前講 Vim 和 Coc 的文章性含。重點(diǎn)注意這個(gè) winnr("$")
洲赵,由于頁面在渲染中做了轉(zhuǎn)義處理,所以拿到的數(shù)據(jù)其實(shí)是 "$"
. 也就是兩個(gè)引號給替換成 "
了商蕴。而我上面用的是 replace 去替換的內(nèi)容板鬓。而在 replace 方法的替換文本中你猜怎么著?$&
代表的是匹配內(nèi)容本身究恤!所以我的實(shí)際替換結(jié)果會是:winnr("f*** mequot;)
...
開始講解咯 ~
其實(shí) Javascript 中字符串的 replace 函數(shù),在傳入?yún)?shù)為字符串時(shí)是有特定的轉(zhuǎn)義字符(變量名)的后德。比如上面的部宿,如果你寫 '$&'
那它就會把你的查找符給替換進(jìn)去。類似的還有其他一些:
$$
是插入一個(gè) "$"瓢湃。
$&
是插入匹配的子串理张。
$`
是插入當(dāng)前匹配的子串左邊的內(nèi)容。
$'
是插入當(dāng)前匹配的子串右邊的內(nèi)容绵患。
$n
假如第一個(gè)參數(shù)是 RegExp 對象雾叭,并且 n 是個(gè)小于 100 的非負(fù)整數(shù),
那么插入第 n 個(gè)括號匹配的字符串落蝙。提示:索引是從 1 開始织狐。如果不存在第 n 個(gè)分組,
那么將會把匹配到到內(nèi)容替換為字面量筏勒。比如不存在第 3 個(gè)分組移迫,就會用“$3”替換匹配到的內(nèi)容。
$<Name>
這里 Name 是一個(gè)分組名稱管行。如果在正則表達(dá)式中并不存在分組(或者沒有匹配)厨埋,
這個(gè)變量將被處理為空字符串。只有在支持命名分組捕獲的瀏覽器中才能使用捐顷。
當(dāng)然你也可以不傳字符串傳函數(shù)進(jìn)去荡陷。這就不展開了雨效,可以到這里仔細(xì)了解:MDN - String.prototype.replace()
換到實(shí)際情況就是因?yàn)槲也迦霠顟B(tài)的錨點(diǎn)是個(gè) script 標(biāo)簽,所以 Html 的內(nèi)容就給截?cái)嗔朔显蕖8缯竟簦╔SS)的原理有點(diǎn)像吧徽龟,所以它才只是頁面下方出現(xiàn)了被斷開的代碼塊而正文的顯示是正常的。
知道是什么問題就好修了嘛蛹头!自己寫一個(gè)不會被任何字符串轉(zhuǎn)義的替換函數(shù)就好了嘛顿肺。
具體怎么寫你可以自己想想看 ??
然后就是開篇的問題,現(xiàn)在知道答案是什么了嗎渣蜗?
'Hello'.replace('ello', '#$&%')
// 'H#ello%'
好了今天就聊到這兒了~