JavaScript中Array.map遍历带重音字符串时全等判断异常问题
问题现象
对包含带重音符号的字符串数组调用.map方法遍历时,会出现视觉上完全一致的字符串相等判断返回false的异常,复现代码如下:
const types: string[] = ['Fée'] types.map(type => { const str = 'Fée' if(str === type){ console.log(`str true : ${str}`) } if(type === 'Fée'){ console.log('type true') } else { console.log(`type false : ${type}`) } })
控制台实际输出:
"str true : Fée" "type false : Fée"
补充特征:异常的重音字符可以正常保存到UTF-8编码的JSON文件中,在VSCode中该异常字符所在行不会被正常语法高亮。
根本原因
问题出在重音字符的Unicode编码差异上:JavaScript的===严格相等是逐字符码位比对,不会自动识别视觉等价但编码不同的字符,而带重音的é存在两种完全合法、渲染效果完全一致的Unicode表示:
- 预组合单码位形式:码位为
U+00E9,本身就是独立的“带尖重音的小写e”字符 - 分解双码位形式:由普通小写e(
U+0065)+ 组合尖重音符号(U+0301)两个码位拼接而成
两种形式视觉上没有任何区别,但码位序列完全不同,所以严格相等判断会返回false。
上述代码里,数组types中存储的Fée、函数内定义的str变量的Fée用的是同一种编码形式,所以第一个判断返回true;而if条件里写的字面量'Fée'用的是另一种编码形式的é,所以第二个判断返回false。VSCode的语法高亮对分解式组合字符的适配存在缺陷,就会出现对应行不高亮的现象。
解决方案
做包含重音、特殊符号的字符串相等判断前,统一调用字符串的.normalize()方法做Unicode归一化,推荐统一使用NFC(标准预组合)格式,将所有视觉等价的字符转为相同码位序列再比对:
const types: string[] = ['Fée'] types.map(type => { const str = 'Fée' // 统一归一化后再做相等判断 if(str.normalize('NFC') === type.normalize('NFC')){ console.log(`str true : ${str}`) } if(type.normalize('NFC') === 'Fée'.normalize('NFC')){ console.log('type true') } else { console.log(`type false : ${type}`) } })
如果需要确认字符串的实际编码,可以用以下代码打印每个字符的十六进制码点,区分两种编码形式:
// 输出码点序列,长度为2说明是预组合形式é,长度为3说明是分解形式é console.log(Array.from(type).map(char => char.codePointAt(0)?.toString(16)))
内容的提问来源于stack exchange,提问作者Ganor
相关产品推荐
相关产品推荐

