JS怎么截取字符串最后两位
在JavaScript中截取字符串最后两位的标准且最简洁的方法是使用 str.slice(-2)。本文系统讲解 slice 负索引底层原理、substring 等传统替代方案的差异、极端边界防御以及包含 Emoji 特殊字符时的处理方案。
str.slice(-2)。传入负整数 -2 时,JS 引擎会自动以字符串末尾为基准向前倒数两位,并截取到字符串结束。
一、核心答案与最简代码示例
JavaScript 提供了多个内置的字符串截取方法,但在处理“从尾部倒数提取”这一需求时,String.prototype.slice() 具备无可替代的优势。它原生支持负数作为索引参数,语义极其简洁明了:
// 1. 常规英文字符串截取
const str = "JavaScript";
const lastTwo = str.slice(-2);
console.log(lastTwo); // 输出: "pt"
// 2. 日期补零与格式化场景(例如提取年份后两位或双位月份)
const dateStr = "2026-11-03";
const day = dateStr.slice(-2);
console.log(day); // 输出: "03"
// 3. 数值补齐双位数截取
const num = "7";
const padded = ("0" + num).slice(-2);
console.log(padded); // 输出: "07"
slice() 方法执行时不会修改原始字符串(JavaScript 中的字符串均为不可变原始值),而是返回包含目标字符的新字符串。
二、底层原理解析:负数索引是如何生效的?
为什么传入 -2 就能精准锁定最后两位字符?这源于 ECMAScript 规范对 String.prototype.slice(beginIndex, endIndex) 参数的标准化定义。
在 JavaScript 中,字符串每个字符都具备两套定位坐标:
- 正向索引(从头开始):从
0依次递增到length - 1。 - 反向负索引(从尾开始):最后一个字符固定为
-1,倒数第二位为-2,倒数第k位即为-k。
当 slice(beginIndex) 的起始参数为负数时,其内部等价于执行了如下换算:
// 引擎内部换算逻辑伪代码
let fromIndex = beginIndex < 0 ? Math.max(str.length + beginIndex, 0) : beginIndex;
例如,若字符串长度为 10,传入 -2 经过计算后为 10 + (-2) = 8。因为没有传递第二个截取终点参数 endIndex,函数默认从索引 8 一直截取到串尾索引 10(左闭右开区间 [8, 10)),正好取得最后两个字符(即索引 8 和 9 处的字符)。
三、常见替代方案与对比说明
除了 slice(-2),部分开发者还会联想到 substring 或 substr。我们必须清楚它们各自的差异与潜在缺陷,避免在实际项目中埋下隐患。
1. str.substring(str.length - 2)
substring 是早期 JavaScript 标准中常用的截取手段。但它有一个致命差异:不支持负数索引。如果在 substring 中直接传入负数,它会默认将负数当作 0 处理(即 str.substring(-2) 等价于 str.substring(0),会直接返回整个完整字符串)。
因此若使用 substring 提取最后两位,必须显式计算字符串长度:
const text = "Frontend";
const res = text.substring(text.length - 2);
console.log(res); // 输出: "nd"
该写法完全可行,但相比 slice(-2) 多写了 text.length -,在代码复用与阅读体验上较为繁琐。
2. str.substr(-2)(已废弃,不推荐)
String.prototype.substr() 已被 ECMAScript 官方规范正式列入 附录 B(Annex B - 仅用于向后兼容的遗留特性),并在 MDN 文档中被标记为 Deprecated。
尽管现代浏览器和 Node.js 运行时为了兼容旧网页仍然保留了 substr,且部分支持负数起始值,但严禁在现代前端或 Node.js 项目中使用该 API,以防在打包工具或严格模式兼容性检测时出现告警或非预期错误。
3. ES2022 的新特性:str.at(-2) + str.at(-1)
在较新的 ECMAScript 2022 规范中,引入了 at() 方法,原生支持负索引单独获取单个字符。如果只需要获取特定的某一个末尾字符,str.at(-2) 是首选;若要获取两位,虽然可以拼合,但依然不如 slice(-2) 直观高效:
const word = "React";
console.log(word.at(-2)); // 输出: "c" (倒数第二位单字符)
console.log(word.at(-1)); // 输出: "t" (最后一位单字符)
四、边界情况与防御式处理
在实际生产业务中,输入数据往往不可完全信任。为了保证程序高健壮性、不抛出未捕获的 Uncaught TypeError,我们需要针对各种边界场景进行防御:
1. 字符串长度小于 2 或为空串
当原字符串长度本身只有 1 个字符或为空串 "" 时,str.slice(-2) 的表现非常安全:
- 对于空串
"".slice(-2),结果为""; - 对于单字符
"A".slice(-2),计算时1 + (-2) = -1,经Math.max(-1, 0)换算后实际从索引0截取到末尾,直接返回"A"。
这说明 slice(-2) 不会发生数组越界崩溃,天然具备容错能力。
2. 输入参数为 null、undefined 或非字符串类型
如果传入的值不是 string(如 null、undefined 或数值),直接调用 .slice() 会抛出 TypeError: Cannot read properties of undefined。因此需在函数入口前进行类型校验或转换:
// 防御式类型转换
function safeGetLastTwo(val) {
if (typeof val !== "string") {
if (val === null || val === undefined) return "";
val = String(val); // 转为字符串
}
return val.slice(-2);
}
3. 特殊字符:包含 Emoji 表情或生僻字(代理对)
JavaScript 内部采用 UTF-16 编码单元,绝大多数 Emoji(如 🚀、🎉)以及部分生僻字在内部由 2 个代理对单元(Surrogate Pair)构成。若字符串末尾恰好包含此类字符,简单的 slice(-2) 可能会截断字符编码,导致乱码显示(如出现 \uD83D 乱码):
const strWithEmoji = "前端🚀";
console.log(strWithEmoji.length); // 长度为 4("前" 1位,"端" 1位,"🚀" 占2个编码单元)
// 普通 slice(-2) 截取的实际上是 Emoji 的双字节单元
console.log(strWithEmoji.slice(-2)); // 输出: "🚀" (仅为一个复合表情,非两个独立字符)
若需求是严格按字素/视见符号(Grapheme / Code Point)截取最后两个独立符号,应先借助 ES6 扩展运算符 [...str] 或 Array.from(str) 展开码点:
const textEmoji = "学习JS🚀🎉";
const lastTwoGraphemes = [...textEmoji].slice(-2).join("");
console.log(lastTwoGraphemes); // 正确输出: "🚀🎉"
五、生产级函数封装与实战速查
综合上述原理与异常保护,我们可以在通用工具库(如 stringUtils.js)中封装一个生产高可用截取函数:
/**
* 安全截取字符串最后两位字符
* @param {any} input - 待截取的目标输入
* @param {boolean} [isUnicode=false] - 是否启用 Unicode 码点解构(应对 Emoji)
* @returns {string} 截取后的结果
*/
function getLastTwoChars(input, isUnicode = false) {
if (input === null || input === undefined) {
return "";
}
const text = typeof input === "string" ? input : String(input);
if (text.length <= 2 && !isUnicode) {
return text;
}
if (isUnicode) {
const charArr = [...text];
return charArr.slice(-2).join("");
}
return text.slice(-2);
}
// 验证测试用例
console.log(getLastTwoChars("Hello World")); // "ld"
console.log(getLastTwoChars("A")); // "A"
console.log(getLastTwoChars("")); // ""
console.log(getLastTwoChars(12345)); // "45"
console.log(getLastTwoChars(null)); // ""
console.log(getLastTwoChars("苹果🍎西瓜🍉", true)); // "西瓜🍉" 对应字符
方法速查表
| 截取方式 | 示例代码 | 负索引支持 | 推荐指数 | 核心适用场景 |
|---|---|---|---|---|
| str.slice(-2) | str.slice(-2) |
原生支持 | ★★★★★ | 绝大多数业务场景的首选,最简语法 |
| str.substring | str.substring(str.length - 2) |
不支持负数 | ★★★☆☆ | 仅在必须兼容古老规范或约束时使用 |
| Array.from + slice | [...str].slice(-2).join('') |
原生支持 | ★★★★☆ | 输入中包含 Emoji、符号或代理对字符时 |
| str.substr | str.substr(-2) |
规范已弃用 | ★☆☆☆☆ | 禁止在现代代码库中使用 |
str.slice(-2) 这一行代码即可解决 99% 的截取字符串末尾两位需求;在封装公共库时,搭配类型兜底与 Unicode 解构即可做到万无一失。
JS语法错误会阻止加载吗?区分SyntaxError与资源加载失败
2026-11-05 16:08:46
JavaScript常见SyntaxError语法错误排查与修复方法
2026-11-05 13:58:44
JS中slice、substring和substr截取字符串的区别
2026-11-05 13:43:47
JS 转 TS 手动迁移最佳实践:从配置 tsconfig 到消除 any 类型
2026-11-05 11:53:32
为什么JS数组push后得到的是数字而不是数组?
2026-11-05 09:59:18








