只要有 substring 和 slice,就沒什麼好怕的。
在加入了陳年專案維護中獲得了新知識。
前言
最近工作派發了一些 Legacy Code 專案翻修,難免會遇到一些 deprecated
的方法。
面對這些陳年代碼,最大的敵人往往都是疏忽了商業邏輯以及代碼可讀性。
本篇重點不在於介紹 substr
, substring
, slice
,想必大家都略懂這方法。
如果使用冷門特性,你一輩子都會記得寫註解對吧!?
這一篇的角度在於筆者對 Legacy Code 進行 String.substr()
重構的思路。
從「維護性」與「效能」的之間找到合適的平衡。
這份筆記也是為了日後仍遇到時,可以快速拿自己的文章參考。
希望能對大家有所幫助。
一、字串切割介紹
簡單說明一下他們「主要」的用法。
最大的區別在於第二個參數「索引 Index」的意義。
String.prototype.substr() - MDN Web Docs
substr(start)
substr(start, length)
String.prototype.substring() - MDN Web Docs
substring(indexStart)
substring(indexStart, indexEnd)
String.prototype.slice() - MDN Web Docs
slice(indexStart)
slice(indexStart, indexEnd)
這些方法中,最大的差異在於:
- 參數使用幾個?
- 傳入的參數值是否為 ≥ 0?
- 傳入的參數值是否為 負數?
二、評估抽換方式
這篇是解決 Legacy Code 的場景,因此可以思考你面對的 substr()
,是為了解決什麼問題?
接著來探索這些可能的條件,找出最適的解決方案!
極端的例子我相信是鮮少的,不太需要執著於極端案例。
難以長期傳承商業邏輯的代碼,最需要的是直觀的可讀性。
以下提供 7 種的例子,作為重構的選擇:
- 基本使用
substr(start, length)
) - 起始索引為負數
substr(-start, length)
→ 使用slice()
- 長度超過字串長度
substr(start, length)
→ 使用substring()
- 起始索引超過字串長度
substr(start, length)
→ 使用slice()
- 單參數
substr(start)
→ 使用substring()
- 起始索引為負數且超過字串長度
substr(-start, length)
→ 使用slice()
- 無法使用
substring(start, end)
替代substr()
的情境 → 使用slice()
1 . 基本使用 substr(start, length)
適用於簡單的範圍擷取,用於單純的商業邏輯。
const str = "JavaScript";
console.log(str.substring(4, 10)); // "Script"
使用 substring()
替換。
console.log(str.substring(4, 10)); // "Script"
✅ substring(start, start + length)
直觀表達了「從 start 位置擷取 length 個字元」,可讀性極高。
2. 起始索引為負數 substr(-start, length)
→ 使用 slice()
substring()
不支援負數索引,因此需要 slice()
來替代。
const str = "Hello World";
console.log(str.slice(-5)); // "World"
使用 slice()
替換。
console.log(str.slice(-5)); // "World"
✅ slice(-start)
直接從倒數第 start 個字元開始擷取,行為與 substr()
相同。
3. 長度超過字串長度 substr(start, length)
→ 使用 substring()
在一些未知長度的條件下,如果允許 length 超出範圍,substr()
會自動調整,substring()
也有類似行為。
const str = "abcdef";
console.log(str.substring(2)); // "cdef"
使用 substring()
替換相對直觀,slice()
你喜歡也可以使用。
console.log(str.substring(2, str.length)); // "cdef"
✅ substring(start, end)
自動處理 end 超過範圍的情況。
4. 起始索引超過字串長度 substr(start, length)
→ 使用 slice()
如果場景具有未知的起始索引,substring()
會自動交換 start 和 end,而有不同結果,這種條件建議使用 slice()
。
const str = "Hello";
console.log(str.slice(10, 13)); // "" (空字串)
使用 slice() 替換。
console.log(str.slice(10, 13)); // "" (空字串)
✅ slice(start, end)
行為與 substr()
一致,會返回空字串。
5. 單參數 substr(start)
→ 使用 substring()
當 length 省略時,substr(start)
會提取到字串結尾,這與 substring(start)
行為相同。這應該是最簡單的場境,單純且單一參數。
const str = "Hello JavaScript";
console.log(str.substring(6)); // "JavaScript"
使用 substring()
替換。
console.log(str.substring(6)); // "JavaScript"
✅ substring(start)
直覺地表示「從 start 開始到字串結尾」,可讀性高。
6. 起始索引為負數且超過字串長度 substr(-start, length)
→ 使用 slice()
通常在於更多的演算方法。substring()
不支援負數索引,應該使用 slice()
。
const str = "Hello";
console.log(str.slice(-10, -7)); // "Hel"
使用 slice()
替換。
console.log(str.slice(-10, -7)); // "Hel"
✅ slice(start, end)
在負索引時表現一致,適用於此場景。
7. 無法使用 substring(start, end)
替代 substr()
的情境 → 使用 slice()
substring()
無法處理負數索引,也無法擷取固定長度,因此 slice()
是更好的選擇。
const str = "Hello World";
console.log(str.slice(-5, -2)); // "Wor"
錯誤的 substring()
替換。
console.log(str.substring(0, 3)); // "" (錯誤,因為 `substring()` 會將負數視為 0)
正確的做法應該用 slice()
。
console.log(str.slice(-5, -2)); // "Wor"
✅ slice()
是唯一能夠完全取代 substr()
在負索引情境下的選擇。
三、結論
整體而言,應該根據重構的目的,以及代碼的職責來決定適合哪一種。
- 基於可讀性優先:如果今天你在業務邏輯單純,且索引範圍固定時,
substring()
可讀性更高。 - 基於效率且容錯率高的場景:如果面對特定的演算或者複雜的計算,甚至索引可能是負數等等不穩定的條件,這時候使用
slice()
相對安全。
適合 substring()
的場景:
- start 與 end 你能肯定商業邏輯為正數。
- 需要擷取 固定範圍,不考慮負索引。
- 可讀性佳,適用於基本的字串擷取。
適合 slice()
的場景:
- 需要支援負數索引
substr(-start, length)
。 - 起始索引超出字串長度時,應返回空字串。
- 避免 start > end 情境時自動交換。
有勘誤之處,不吝指教。ob'_'ov