为何'❌'[0]==='❌'成立但'✔️'[0]==='✔️'不成立?Unicode相关问题
底层运行逻辑
JavaScript字符串采用UTF-16编码存储,所有默认的字符串操作(包括下标取值、split('')拆分)都是按16位的UTF-16码元处理,而非用户视觉上感知的单个「字符」(Unicode字素簇)。
两个符号的编码差异直接导致了操作结果不同:
❌对应单个Unicode码点U+274C,仅占1个UTF-16码元,因此'❌'[0]可以取到完整的符号,比较返回true。✔️是由两个码点组合而成的字素簇:基础符号为✔(U+2714,默认渲染为黑白文本样式),后接变体选择符U+FE0F,作用是指定该符号渲染为彩色emoji样式。整个✔️占2个UTF-16码元,因此'✔️'[0]只能取到前半部分的基础符号✔,和完整的✔️比较返回false,和单独的✔比较返回true。
你遇到的统计结果异常也源于此:split('')会把'✔️❌✔️❌'按码元拆分为['✔', '️', '❌', '✔', '️', '❌'],数组中不存在完整的✔️元素,因此统计长度为0,而❌是单个码元,拆分后正常存在,统计结果为2。
正确处理方案
如果要按视觉字符做比较、统计操作,需要使用支持Unicode字素簇处理的API:
- 字符统计推荐使用内置的
Intl.Segmenter接口,专门用于按视觉字素簇拆分字符串:
// 初始化字素簇分割器 const segmenter = new Intl.Segmenter('zh', { granularity: 'grapheme' }); // 拆分得到视觉字符数组 const charList = Array.from(segmenter.segment('✔️❌✔️❌'), item => item.segment); console.log(charList.filter(e => e === '❌').length) // 输出2 console.log(charList.filter(e => e === '✔️').length) // 输出2
- 单独比较两个视觉字符是否相等时,可以先调用
normalize()方法做Unicode规范化后再比较:
'✔️'.normalize() === '✔️'.normalize() // 输出true
内容的提问来源于stack exchange,提问作者Wilhelm Olejnik
相关产品推荐
相关产品推荐

