JavaScript怎么截取字符串的前N位和后N位

作者:半夏时间:2026-11-02 10:25:50浏览:653

详细解析在JavaScript中截取字符串前N位与后N位的标准方法、底层索引机制、slice与substring的差异对比,以及处理边界情况与Unicode表情字符的实战指南。

JavaScript怎么截取字符串的前N位和后N位
核心结论速览:在现代 JavaScript 中,截取字符串前 N 位和后 N 位的最佳标准方法是使用 String.prototype.slice()。截取前 N 位写作 str.slice(0, n);截取后 N 位写作 str.slice(-n)。当截取后 N 位且传入的 N 可能为 0 时,建议写成 n <= 0 ? '' : str.slice(-n) 以防止误返回完整原串。

在日常前端与 Node.js 业务开发中,提取字符串开头或结尾的若干字符是高频操作,例如手机号脱敏截取、文件拓展名前缀判定、流水号后四位提取或卡号展示等。虽然 JavaScript 提供了多种截取字符串的原生 API,但不同方法在负数索引处理、边界溢出控制以及参数语义上存在明显差异。本文将系统梳理标准解决方案、底层索引模型与生产边界防护。

一、核心标准方法:使用 slice() 高效截取

slice(startIndex, endIndex) 是 ECMAScript 标准中最推荐使用的字符串切片方法。它提取原字符串的一个子片段,并返回一个新字符串,而绝不修改原字符串(JavaScript 字符串本身是不可变的)。

1. 切片的核心规则:左闭右开

slice() 接收两个参数:第一个参数为提取的起始位置 startIndex(包含该位置字符),第二个参数为结束位置 endIndex(不包含该位置字符),即数学上的半开半闭区间 [startIndex, endIndex)。如果省略第二个参数,则默认截取到字符串的末尾。

JavaScript怎么截取字符串的前N位和后N位
图 1:JavaScript 字符串正向索引、反向负数索引及 slice() 的提取区间映射关系

2. 截取前 N 位的具体代码

由于 JavaScript 字符串索引从 0 开始计数,截取前 N 位即代表从索引 0 提取到索引 n 之前:

// 示例:截取前 N 位
const text = "JavaScript";
const n = 4;

const firstN = text.slice(0, n);
console.log(firstN);
示例输出
"Java"

当 n 大于或等于字符串总长度时,slice(0, n) 并不会抛出异常,而是优雅地返回完整原字符串副本;如果 n 为 0,则返回空字符串 ""。

3. 截取后 N 位的具体代码

slice() 原生支持负数参数。当传入负数时,JavaScript 会将其自动换算为 length + index,其中 -1 表示最后一个字符,-n 表示倒数第 n 个字符。因此,仅需传入 -n 即可直接截取末尾 N 个字符:

// 示例:截取后 N 位
const text = "JavaScript";
const n = 4;

const lastN = text.slice(-n);
console.log(lastN);
示例输出
"ript"

二、传统方案辨析:为什么不用 substring() 与 substr()

初学者常常困惑于 slice()、substring() 与 substr() 三者之间的区别。在实现“前N位”与“后N位”这一具体需求时,它们表现出完全不同的参数解析逻辑。

JavaScript怎么截取字符串的前N位和后N位
图 2:slice()、substring() 与已废弃的 substr() 参数处理逻辑对比

1. substring() 为什么不适合截取后 N 位

substring(start, end) 同样接收起始和结束索引,但它有两个违背直觉的特性:

  • 负数一律转为 0:若传入任何小于 0 的数值,substring() 会强制将其当成 0 处理。因此 str.substring(-4) 实际上等价于 str.substring(0),返回的是全部字符串,完全无法实现反向切片。
  • 参数自动颠倒:若 start > end,substring() 会自动交换这两个实参。例如 str.substring(4, 0) 会变成 str.substring(0, 4)。这种“宽容”的设计在大型项目中经常会掩盖逻辑计算错误。

如果坚持使用 substring() 截取后 N 位,必须手动引入字符串长度做减法运算:str.substring(str.length - n),代码冗长且不易维护。

2. substr() 已被标准弃用

substr(start, length) 的第二个参数代表截取长度。尽管早期它支持负数起始索引(如 str.substr(-4) 能够截取后4位),但该方法已被 ECMAScript 官方规范正式标记为已弃用(Annex B Legacy)。现代 Web 规范强烈建议不要在任何新项目中继续使用 substr(),未来代码应全面迁移至 slice()。

三、严谨落地的实操步骤与边界防御

在编写高质量生产代码或公共工具函数时,除了知道最简语法,还需要考虑到入参类型异常、数值越界及负零陷阱等情况。

JavaScript怎么截取字符串的前N位和后N位
图 3:字符串截取操作执行步骤与边界处理流程

1. 警惕 N = 0 时的负零陷阱(-0 === 0)

这是 JavaScript 字符串操作中最容易被忽略的隐蔽 Bug。当截取后 N 位且传入的变量 n = 0 时:

// 隐患示例:
const str = "Hello";
const n = 0;

// 在 JS 中,-0 完全严格等于 0
console.log(-n === 0); // true

// 因此 str.slice(-0) 实际上等同于 str.slice(0)!
const result = str.slice(-n);
console.log(result); // 输出 "Hello",而不是期望的 ""!
示例输出
"Hello"

由于数学上 -0 等同于 0,导致 str.slice(-0) 并不会从末尾倒数,而是被判定为正向索引 0,从而把整个字符串完整返回。为了解决这个问题,提取后 N 位时必须对 n <= 0 进行防御截断。

2. 工业级通用截取函数封装

下面给出严谨防错的前 N 位与后 N 位工具函数实现:

/**
 * 安全截取字符串的前 N 个字符
 * @param {string} str 待处理的字符串
 * @param {number} n 截取的字符位数
 * @returns {string}
 */
function takeFirstN(str, n) {
  if (typeof str !== 'string' || !n || n <= 0) {
    return '';
  }
  return str.slice(0, n);
}

/**
 * 安全截取字符串的后 N 个字符(防御 N=0 陷阱)
 * @param {string} str 待处理的字符串
 * @param {number} n 截取的字符位数
 * @returns {string}
 */
function takeLastN(str, n) {
  if (typeof str !== 'string' || !n || n <= 0) {
    return '';
  }
  return str.slice(-n);
}

// 执行验证
const sample = "Order-20261101-9876";

console.log("前5位:", takeFirstN(sample, 5));
console.log("后4位:", takeLastN(sample, 4));
console.log("后0位测试:", takeLastN(sample, 0));
console.log("超长位数测试:", takeFirstN(sample, 100));
示例输出
前5位: "Order"
后4位: "9876"
后0位测试: ""
超长位数测试: "Order-20261101-9876"

四、进阶场景:正确处理 Emoji 表情与特殊 Unicode 字符

在国际化场景或包含 Emoji 表情(如社交评论、昵称展示)时,直接使用 str.slice() 可能会造成字符截断乱码。这是因为 JavaScript 的字符串原生索引基于 UTF-16 代码单元(Code Units),而不是视觉上的“字符”。

1. 代理对(Surrogate Pairs)截断隐患

常用字符(如 ASCII 和常规汉字)属于基本多语言平面(BMP),占用 1 个 UTF-16 码元;而许多 Emoji 表情(例如 🎉、🚀)以及生僻汉字占用 2 个码元(代理对)。

// 代理对截断问题剖析
const emojiStr = "🎉新年快乐";
console.log(emojiStr.length); // 输出 5,因为 "🎉" 自身占用 2 个码元

// 错误示范:如果试图截取前 1 位
const broken = emojiStr.slice(0, 1);
console.log(broken); // 输出乱码字符 "\uD83C"(孤立高位代理项)
示例输出
"" (无效的高位代理项乱码)

2. 解决方案:使用码点迭代器(Array.from 或扩展运算符)

ES6 引入了 Unicode 码点识别机制。通过 Array.from(str) 或 [...str] 可以按完整的 Unicode 码点解构字符串,再执行切片即可保证 Emoji 结构完整无损:

// 优雅截取支持 Emoji 的前 N 位与后 N 位
function safeUnicodeTakeFirstN(str, n) {
  if (typeof str !== 'string' || !n || n <= 0) return '';
  return Array.from(str).slice(0, n).join('');
}

function safeUnicodeTakeLastN(str, n) {
  if (typeof str !== 'string' || !n || n <= 0) return '';
  return Array.from(str).slice(-n).join('');
}

const msg = "🎉恭贺新禧🎊";
console.log("安全前2位:", safeUnicodeTakeFirstN(msg, 2));
console.log("安全后2位:", safeUnicodeTakeLastN(msg, 2));
示例输出
安全前2位: "🎉恭"
安全后2位: "禧🎊"

五、总结与选型速查

JavaScript怎么截取字符串的前N位和后N位
图 4:JavaScript 字符串截取最佳实践方案速查矩阵

为了便于在不同业务场景中快速选型,下表对各类方案的技术特性进行综合归纳:

需求场景 推荐方案 时间复杂度 核心优势与注意事项
常规前 N 位 str.slice(0, n) O(n) 执行速度最快,标准内置,内存友好。
常规后 N 位 n <= 0 ? '' : str.slice(-n) O(n) 原生支持负数倒数,注意防范 n=0 的全量反弹。
包含 Emoji 表情前 N 位 Array.from(str).slice(0, n).join('') O(L) 按 Unicode 码点拆分,彻底消除高低位代理对半截乱码。
历史代码重构 替换废弃的 substr() 为 slice() - 消除 Annex B 废弃警告,统一 API 调用范式。

掌握 slice() 的左闭右开区间与负数索引换算机制,是写出健壮、高效且可读性强的 JavaScript 字符串处理逻辑的坚实基础。

作者最新文章
相关文章
更多