-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
你还在用charCodeAt那你就out了 #4
Labels
Comments
嗯嗯,这个是emoji的组合字符,这是另外一个规则 |
@Jiasm 是的,数组和数组不想等,我改一下吧。 console.log([..."👨👩👧👦"])
// ["👨", "", "👩", "", "👦", "", "👦"] |
这里有点麻烦,因为ascii以外的其实只能用于标识符。但是读者拿🀄是会试验失败的( |
这个的确是。。。我改一下,这块可以单独拿出来讲。 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
在JavaScript中处理中文和其他Unicode字符时,我们会用到处理Unicode相关的API。
在早期,JavaScript提供的
String.prototype.charCodeAt
和String.fromCharCode
就是能够将字符串转换为Unicode的UTF-16编码以及从UTF-16编码转换为字符串的函数。比如:
这里我们将字符串展开成单个字符,再通过charCodeAt方法将字符串转换为对应的Unicode编码,这里的20013和25991就是“中文”两个字对应的Unicode编码。
同样,我们可以使用fromCharCode将Unicode编码转换为字符串:
这两个方法相信大部分同学都不陌生,这是从ES3就开始支持的方法。但是,这个方法在今天我们处理Unicode字符时不够用了。
为什么呢?我们来看一下例子:
这个字符是我们熟悉的麻将中的红中,现在很多输入法都能直接打出来,看上去似乎也正常,没什么问题啊?
可你再试试:
实际上Unicode字符🀄的UTF-16编码并不是55356,这时候如果你使用charCodeAt来得到字符🀄的UTF-16编码,应该要到两个值:
对应的
String.fromCharCode(55356, 56324)
才能还原🀄字符。除此以外,还有其他一些不一样的地方,比如:
👉🏻知识点:Unicode标准中,将字符编码的码位以
2**16
个为一组,组成为一个平面(Plane),按照字符的码位值,分为17个平面,所有码位从0x000000到0x10FFFF,总共使用3个字节。其中最前面的1个字节是平面编号,从0x0到0x10,一共17个平面。
第0号平面被称为基本多文种平面(BMP,Basic Multilingual Plane),这个平面的所有字符码位只需要16位编码单元即可表示,所以它们可以继续使用UTF-16编码。
其他的平面被称为辅助平面(supplementary plane),这些平面的字符被称为增补字符,它们的码位均超过16位范围。
ES5及之前的JavaScript的Unicode相关API,只能以UTF-16来处理BMP的字符,所有字符串的操作都是基于16位编码单元。
因此,当🀄这样的增补字符出现时,得到的结果就会与预期不符。
在ES2015之后,JavaScript提供了新的API来支持Unicode码位,所以我们可以这么使用:
👉🏻 知识点:
String.prototype.codePointAt(index)
方法返回字符串指定index位置的字符的Unicode码位,与旧的charCodeAt方法相比,它能够很好地支持增补字符。对应地,我们有
String.fromCodePoint
方法将CodePoint转为对应的字符:Unicode 转义
JavaScript字符串支持Unicode转义,所以我们可以用码位的十六进制字符串加上前缀
\u
来表示一个字符,例如:0x4e2d
和0x6587
分别是20013和25991的十六进制表示。注意,Unicode转义不仅仅可以用于字符串,实际上\uxxxx也是可以用在标识符,并相互转换的。例如我们可以这么写:
上面的代码我们定义了一个中文变量,声明的时候我们用Unicode转义,console.log的时候用它的变量名字符,这样也是没有问题的。
\u和十六进制字符的这种表示法同样只适用于BMP的字符,所以如果我们试图使用它转义增补字符,直接这样是不行的:
这样,引擎会把
\u1f004
解析成字符\u1f00
和阿拉伯数字4组成的字符串。我们需要使用{}
将编码包含起来,这样就可以了:代理对(surrogate pair)
为区别BMP来表示辅助平面,Unicode引入代理对(surrogate pair),规定用2个16位编码单元来表示一个码位,具体规则是将一个字符按如下表示:
其中110110yyyyyyyyyy和110111xxxxxxxxxx就是两个代理字符,形成一组代理对,其中第一个代理字符的范围从U+D800到U+DBFF,第二个代理字符的范围从U+DC00到U+DFFF。
实现getCodePoint
理解了代理对,我们就可以通过charCodeAt实现getCodePoint了:
同样地,我们也可以通过fromCharCode实现fromCodePoint:
所以我们就可以用上面这样的思路来实现早期浏览器下的polyfill。实际上MDN官方对codePointAt和fromCodePoint的说明中,就按照上面的思路提供了对应的polyfill方法。
getCodePointCount
JavaScript字符串的length只能获得UTF-16字符的个数,所以前面看到的:
要获得Unicode字符数,有几个办法,比如使用spread操作是可以支持Unicode字符串转数组的,所以:
或者使用带有u描述符的正则表达式:
扩展
Unicode码位使用固定的4个字节来编码增补字符,而早期,UTF-8编码则采用可变的1~6个字节来编码Unicode字符。
UTF-8编码方式如下:
在浏览器的encodeURIComponent和Node的Buffer默认采用UTF-8编码:
这里的E4、B8、AD就是三个字节的十六进编码,我们试着转一下:
我们将三个字节的控制码1110、10、10分别去掉,然后将它们按照从高位到低位的顺序拼接起来,正好就得到'中'的码位20013。
所以我们也可以利用UTF-8编码规则,写另一个版本的通用方法来实现getCodePoint:
那么同样,我们可以实现fromCodePoint:
关于Unicode,你还有什么想讨论的,欢迎在issue中留言。
The text was updated successfully, but these errors were encountered: