只要有 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