Unicode in JavaScript
照我的理解,Unicode为每一个字符指定了一个数字,称为code point。
const cp = 'a'.codePointAt(0)
console.log(cp)
console.log(String.fromCodePoint(cp))
// cp的16进制
console.log('\x61')
JavaScript内部使用UTF-16,每个code point用一到两个16bit的code unit编码。BMP(0–65535)里的code point用一个code unit表示,BMP之外的code point用两个code unit表示,称为surrogate pair。BMP包含绝大部分常用的字符,包括汉字。
const cp = '我'.codePointAt(0) console.log(cp) console.log(String.fromCodePoint(cp))
而emoji则位于BMP之外。许多JavaScript的原生方法作用于code unit,处理emoji时,会产生一些奇怪的结果。
const cp = '😀'.codePointAt(0)
console.log(cp)
console.log(String.fromCodePoint(cp))
console.log('😀'.length)
console.log('😀'.split(''))
console.log('😀'[0])
显然length和split只能处理code unit,😀由两个code unit表示,因此长度为2。Surrogate pair里的code unit单独存在时没有任何意义。一些生僻字同样在BMP之外。
const cp = '𬊈'.codePointAt(0)
console.log(cp)
console.log(String.fromCodePoint(cp))
console.log('𬊈'.length)
codePointAt与String.fromCodePoint作用于code point,以下一些方法也能处理code point。
console.log(Array.from('😀'))
console.log([...'😀'])
for (const a of '😀') {
console.log(a)
}
显然,以index来循环,だめ。
const a = '😀'
for (let i = 0; i < a.length; i++) {
console.log(a[i])
}
另有一些被称为grapheme cluster的字符,这些字符看起来、我们会把它们理解为一个字符,但实际上由多个code point构成。例如🖖🏻即由两个code point构成。
const s = "🖖🏻" console.log(s.length) console.log([...s]) console.log(String.fromCodePoint(s.codePointAt(0), s.codePointAt(2)))
目前JavaScript没有原生的方法可以处理grapheme cluster。
A not so interesting tidbit
在Chrome的控制台里输入🖖🏻,需要按两次删除键才能删掉,第一下只会删掉第二个code point,留下了🖖。