JavaScript正則表達式replace的一個坑
- 2021 年 11 月 7 日
- 筆記
- javascript, 學習筆記, 正則表達式
經常聽大家說JavaScript是魔法語言,咱卻沒有什麼深刻體會。直到這回踩到這個坑,我終於醒悟了,JavaScript果然來自霍格沃茨!
0x00 踩到坑
昨天咱經過一番考慮後決定將 Python正則表達式細節小記 這篇筆記發到個人部落格上。選好文章音樂,複製markdown內容…發布!
按照慣例我檢查了一下發布後的文章內容,然後就見到了一個奇怪的現象:
文章內容到一半的時候全被替換成 模板里的HTML 了…
之前調試部落格的時候從沒遇到過這個問題,我一時就有點摸不著頭腦( >﹏<。),但沒關係,比對一下原文檔就應該知道問題在哪了:
很明顯能發現是從$
開始被替換了,我心裡咯噔一下:怕不是和正則表達式有關!不過在排查正則表達式之前我去改了一下部落格後台部分的程式碼:
結果問題仍然存在,我接著還拿著這樣的內容片段進行復現,但沒能成功($
並沒有被替換成其他內容):
test$
$
test$
0x01 坑在哪
到最後,我還是懷疑回了正則表達式,但想來想去還是摸不著頭腦,正則表達式和用作替換的字元串有什麼關係,$
不是用在正則表達式里的嗎?而且為什麼刻意用$
去復現又不行呢?
實在不行只能去求助一下某搜索引擎了:
不查不知道,一查嚇一跳,看到有老哥提到了replace函數接收的字元串不僅僅是字元串,我趕緊去MDN查了一下:
原來用作替換的字元串內能包括一些特殊的變數名!
變數名 | 代表的值 |
---|---|
$$ | 插入一個 “$”。 |
$& | 插入匹配的子串。 |
$` | 插入當前匹配的子串左邊的內容。 |
$’ | 插入當前匹配的子串右邊的內容。 |
$n | 假如第一個參數是 RegExp對象,並且 n 是個小於100的非負整數,那麼插入第 n 個括弧匹配的字元串。提示:索引是從1開始。如果不存在第 n個分組,那麼將會把匹配到到內容替換為字面量。比如不存在第3個分組,就會用「$3」替換匹配到的內容。 |
$ |
這裡Name 是一個分組名稱。如果在正則表達式中並不存在分組(或者沒有匹配),這個變數將被處理為空字元串。只有在支援命名分組捕獲的瀏覽器中才能使用。 |
表格原地址: //developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replace#使用字元串作為參數
現在再回去看文章markdown內容,有一部分我是這樣寫的:
到了這裡,我發現老師說的在```[]```中**被當作普通字元**的元字元只是一部分罷了,主要是 ```*```,```?```,```+```,```{}```,```()```,```$``` 這些元字元。
毫無疑問其中的$\`
就被替換為了匹配字串左邊的內容,也就是模板的前面一部分,才導致文章被處理成這樣。
可以說真的非常魔法了,萬萬沒想到JavaScript竟然在待替換字元串這裡內置了一些$
變數名的用法。要是我沒有想著把這篇小記發到個人部落格上,說不定還得要好一陣子才能發現這個問題。
0x02 解決方法
解決方法其實很簡單,str.replace(regexp|substr, newSubStr|function)
的第二個參數是可以接受一個函數的,而這個函數的 返回值 就被直接用作匹配項替換了,而不是先尋找一遍$
變數名。
比如我原來是這樣寫的:
str.replace(new RegExp('\\{\\[' + from + '\\]\\}','gi'), to);
那麼我用箭頭函數改寫一下就行了:
str.replace(new RegExp('\\{\\[' + from + '\\]\\}','gi'), ()=>to);
其實就相當於:
str.replace(new RegExp('\\{\\[' + from + '\\]\\}','gi'), function(){
return to;
});
關於這個函數傳入的參數可以看MDN文檔這裡的 指定一個函數作為參數
0x03 教訓
以後寫JavaScript程式碼的時候還是不能掉以輕心了,說不定在哪個角落還有我不太清楚的魔法。遇到不會或者不清楚的一定要多查文檔,不然一旦寫進項目里可能就會成為一個遺留的潛在問題。(ノへ ̄、)