都2025年了,你还在用 `substr`?

126 阅读2分钟

当面试官看到你写出 str.substr(2, 3) 的那一刻,他的表情管理直接破防——毕竟 MDN 早在 2021 年就给它打了 “废弃” 标签,而 2025 年的今天,浏览器控制台甚至贴心地给你飘黄:substr is deprecated

别慌,这篇 5 分钟速通指南,带你把 “字符串截取” 从史前时代拉到现代前端:

  • 安全截断 emoji ✂️
  • 多字节字符不翻车 🚑
  • 一行代码搞定 UI 展示 📏

sliceIntl.Segmenter,再到视觉 line-clamp,全部打包带走!

产品:“只要保留前 20 个字,后面打点!”
后端:“UTF-8 字符截断别炸!”
测试:“emoji 不能劈成两半!”

别急,把这篇存书签,下次谁来都能一句话甩答案。


🗂 速查表

方法是否破坏 Unicode是否支持负数备注
slice最常用
substring参数自动交换
substr已废弃
split + join适合按分隔符
Intl.Segmenteremoji/复合字符安全
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.fromIntl.Segmenter
  • 多行省略号:CSS line-clamp
    把工具函数复制进 utils,下次产品再提“前 20 字”需求,直接甩过去!