当面试官看到你写出 str.substr(2, 3) 的那一刻,他的表情管理直接破防——毕竟 MDN 早在 2021 年就给它打了 “废弃” 标签,而 2025 年的今天,浏览器控制台甚至贴心地给你飘黄:substr is deprecated!
别慌,这篇 5 分钟速通指南,带你把 “字符串截取” 从史前时代拉到现代前端:
- 安全截断 emoji ✂️
- 多字节字符不翻车 🚑
- 一行代码搞定 UI 展示 📏
从 slice 到 Intl.Segmenter,再到视觉 line-clamp,全部打包带走!
产品:“只要保留前 20 个字,后面打点!”
后端:“UTF-8 字符截断别炸!”
测试:“emoji 不能劈成两半!”
别急,把这篇存书签,下次谁来都能一句话甩答案。
🗂 速查表
| 方法 | 是否破坏 Unicode | 是否支持负数 | 备注 |
|---|---|---|---|
slice | ❌ | ✅ | 最常用 |
substring | ❌ | ❌ | 参数自动交换 |
substr | ❌ | ✅ | 已废弃 |
split + join | ❌ | ✅ | 适合按分隔符 |
Intl.Segmenter | ✅ | ✅ | emoji/复合字符安全 |
Array.from | ✅ | ✅ | 码点级别 |
CSS line-clamp | ✅ | — | 视觉截断 |
1️⃣ slice —— 90% 场景够用
const str = 'Hello 前端🥷';
str.slice(0, 7); // "Hello 前"
str.slice(-3); // "🥷"
左闭右开,负数倒着数,不会把 emoji 劈开(因为 JS 字符串 UTF-16 码元,🥷 占 2 个码元,但 slice 按码元截)。
2️⃣ substring —— 不会负数
'abcdef'.substring(2, 4); // "cd"
'abcdef'.substring(4, 2); // 自动换序 → "cd"
与 slice 差异:负数变 0,不推荐。
3️⃣ substr —— 别用
'abcdef'.substr(2, 3); // "cde" 第二个参数是长度
MDN 已标废弃,新项目直接 pass。
4️⃣ split + join —— 按分隔符截
const csv = 'a,b,c,d,e';
csv.split(',', 3).join(','); // "a,b,c"
5️⃣ Array.from —— 码点级安全
const str = '👨👩👧👦family';
Array.from(str).slice(0, 6).join(''); // "👨👩👧👦fa"
Array.from会把每个 码点 变成数组元素,连复合 emoji 也当 1 位。
6️⃣ Intl.Segmenter —— 终极武器
const seg = new Intl.Segmenter('zh', { granularity: 'grapheme' });
const segments = [...seg.segment('👨👩👧👦好')];
segments.slice(0, 2).map(s => s.segment).join(''); // "👨👩👧👦好"
浏览器 ≥ 94,按“人类肉眼可见字符”分割,任何 emoji、连字、变音符号都不会断。
7️⃣ CSS 视觉截断 —— 不碰 JS
.truncate {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
纯视觉,不破坏 DOM,适合多行省略号。
🎯 实战:20 字打点(含 emoji 安全)
function truncate(str, len = 20) {
const seg = new Intl.Segmenter('zh', { granularity: 'grapheme' });
const arr = [...seg.segment(str)].map(s => s.segment);
return arr.length > len ? arr.slice(0, len).join('') + '…' : str;
}
truncate('👨👩👧👦前端开发真香警告⚠️', 10);
// "👨👩👧👦前端开发真…"
🏁 一句话总结
- 普通英文/数字/中文:
slice足够 - 带 emoji/复合字符:
Array.from或Intl.Segmenter - 多行省略号:CSS
line-clamp
把工具函数复制进 utils,下次产品再提“前 20 字”需求,直接甩过去!