從一則案例解析js正則的String物件的replace方法使用技巧

Leo_縱化發表於2019-02-20

首先介紹一下業務需求:

在實時輸入過程中,&&、||和-三個符號中的任意兩個(包括自身重複在內)不能直接或通過空格連在一起,否則只保留第一個。

思路1:窮舉

這是我第一時間想到的方法,窮舉這三個符號任意兩個符號的排列組合,然後一一替換。根據排列組合公式可知一共有6種不同的組合方式,需要寫6個正則。
這種方法的好處是易於理解,但缺點也顯而易見,程式碼臃腫,難以維護。

思路2:寫一個匹配出所有組合的正則,然後進行替換。

首先,寫出這個正則

let reg = /((&&|\|\||-)\s*){2,}/g
複製程式碼

這個正則的意思是,匹配任意這三個符號(&& || -)加零個或多個空格出現兩次以上的文字。
有了這個正則,接下來就是如何替換了,首先想到的是正則的反向引用了$1($1的意思是第一個分組的內容,正則中括號()代表分組,即((&&|\|\||-)\s*)

先來分析一下這個正則的匹配過程,我們寫個測試案例來演示

'|| -'.replace(/((&&|\|\||-)\s*){2,}/g,"\$1")   // -
複製程式碼

匹配過程是這樣的:

  • 從左到右遍歷這個字串,找到 "|| "符合第一個分組內容,但不符合{2,}(出現兩次以上), 此時$1分組的內容是"|| "
  • 繼續遍歷,匹配到"-"符合規則,此時$1分組的內容是"-"
  • 這時已滿足(&&|\|\||-)\s*)同時出現兩次以上的規則,開始替換,將整個字串替換成$1,即-,所以輸出為"-"

那麼問題來了,業務的需求是替換成第一個字元,而我們卻替換成了最後一個字元。彆著急,先喝口水冷靜一下。其實我們只要把原字串翻轉一下,再傳進來不就符合要求了嗎,替換完畢後記得把字串翻轉回去就行了

// 翻轉字串
function rever(str) { 
  return str.split("").reverse().join("")
}
複製程式碼

完整程式碼如下:

let str = 'ccc && ||'
function rever(str) {
  return str.split("").reverse().join("")
}
let newStr = rever(rever(str).replace(/((&&|\|\||-)\s*){2,}/g,"\$1"))
console.log(newStr);
複製程式碼

程式碼寫出來了,測試結果也符合預期,那麼就大功告成了嗎?
看看這段程式碼的實現原理,是把原字串翻轉,然後進行匹配替換,替換完在把字串翻轉回來。 程式碼執行過程中,是對原字元進行了修改的。如果放到生產環境中,由於使用者的輸入習慣不同,可能會造成某些不可預期的錯誤。

另闢蹊徑:

replace的第二個引數,除了傳入字串,不是還可以傳入一個函式嗎?我們只要在這個函式中,把符合業務需要的第一個字元找出來,然後return 出去,不就完事了嗎?
用這種方式處理,不會汙染原字串,沒有副作用。不知道算不算得上最優解呢?
程式碼很簡單,大家看一下應該就明白了:

let str = 'ccc && ||'
let newStr = str.replace(/((&&|\|\||-)\s+){2,}/g, function(a){
  return a.substr(0, a.indexOf(' ')) + ' ';
});
console.log(newStr);  // "ccc && "
複製程式碼

PS:點個贊再走吧~

相關文章