JavaScript 字符串按字符拆分
split('')`、展开运算符和 `Array.from()` 在 Unicode 字符串拆分上的差异。
#type / concept
#status / growing
#resource / javascript
#resource / ecmascript
[!info] related notes
JavaScript 字符串按字符拆分
这个问题的核心不是“怎么拆字符串”,而是“按什么单位拆”。split('') 按 UTF-16 代码单元拆,[...str] 和 Array.from(str) 则按字符串迭代器产出的码位拆。
先看最直观的差异
const str = 'a🚀b';
str.split('');
// ['a', '\uD83D', '\uDE80', 'b']
[...str];
// ['a', '🚀', 'b']
Array.from(str);
// ['a', '🚀', 'b']
为什么 split('') 会出问题
JavaScript 字符串底层基于 UTF-16。像 🚀 这样的字符会占用两个代码单元。
split('') 只会机械地逐个代码单元切开,所以会把代理对拆散。
为什么 [...str] 和 Array.from(str) 更稳妥
它们消费的是字符串默认迭代器,而字符串迭代器会按码位产出值,所以能正确处理大多数超出 BMP 的字符。
这也是它们和 ecmascript-iterators-and-generators 直接相关的地方。
但这里还有一个边界
[...str] 和 Array.from(str) 比 split('') 更好,但它们也不等于“完整的用户感知字符切分”。
比如:
- 旗帜 emoji
- 家庭 emoji
- 某些带组合附加符号的字符
这些可能由多个码位组成一个肉眼看到的字符。真要按“用户看到的一个字符”切分,往往要看 Intl.Segmenter 等更高层方案。
什么时候用哪种方式
- 已知只处理 ASCII 或简单英文时,
split('')可以工作 - 只想避免代理对被拆坏时,优先
[...str]或Array.from(str) - 需要严格的人类可见字符分段时,考虑
Intl.Segmenter
延伸阅读
- 看字符串迭代为什么成立:ecmascript-iterators-and-generators
- 看字符串与
RegExp、split()的配合:ecmascript-regular-expressions - 看拆分后常见的数组处理:javascript-array