JavaScript中字符编码的基本知识
在JavaScript中,所有的编码都是采用UTF-16进行编码的(大多数引擎,少部分使用UCS-2),也就是2个字节(16位)表示一个编码单元。
大多数情况下,一个编码单元就可以表示一个完整的字符,如英文字母,数字等;但16位能表示的编码单元有限,有些字符需要通过两个编码单元才能表示一个字符,如汉字,特殊字符等。一个编码单元就能表示的字符被称为基本面(Basic Multilingual Plane),需要两个编码单元的字符被称为补充面(Supplementary planes or Astral planes)。
这样,基本面和补充面使得所有字符都对应一个唯一标识符,这种标识符被称为码点(Code Point)。
ES5中的问题
在ES5中,所有对于字符的操作都是基于16位的,那么当遇到超过这个范围的字符时就变得无能为力了。
1 | var ch = '🇨🇳'; |
可以看出,上面所有的操作都没有把ch当做一个字符去处理。
当对象是一个由两个16位编码单元组成的代理对(Representation)字符时,
首先,长度为4;
正则表达式测定两个字节也为false
;
而charAt()
也不能处理字符串中第一次出现代理对字符之后的所有字符(注意出现之前是可以的);
最后charCodeAt()
也将一个代理对字符当做两个16位字符对待。
ES6中的解决办法
在ES6中,对于代理对字符新增了相关的字符串处理方法String.codePointAt()
和String.fromCodePoint()
。从字面意思就可以知道,第一个方法是用于查找码点所在位置,第二方法是将一个码点位置转换为编码的,这两个方法都对代理对字符有效。
string.codePointAt()
1 | const chStr = '🇨🇳'; |
从对下标为2的字符处理的区别可以看出,string.codePointAt()
确实将一个代理对字符当做一个码点处理。
String.fromCodePoint()
1 | console.log(`${String.fromCodePoint(127464)}${String.fromCodePoint(127475)}`); // 🇨🇳 |
一个代理对码点也通过String.fromCodePoint()
正确地转换成了代理对字符。
正则表达式
ES6中定义了一个新的正则标志u,用于处理Unicode字符。
1 | console.log(/^(..)$/.test('🇨🇳')); // false |
从上面的代码我们可以看出加上u之后,正则表达式将'🇨🇳'
看成了一个完整的字符,正则将字符串中的字符视作基本操作单位,而不是编码单元作为基本操作单位。
转义代理对字符
在ES5中,前缀加\u
可以将Unicode转码,但是并不能正确处理代理对字符。在ES6中可以正确处理了,只需要在ES5的基础上加上一对大括号就可以正确转码。
1 | console.log('\u{1f1e8}\u{1f1f3}'); // 🇨🇳 |
知识点总结
- 了解JavaScript的基本编码知识;
- 熟练使用ES6中codePointAt()和fromCodePoint()两个处理代理对字符的函数;
- 熟练使用ES6中正则表达式中处理Unicode的u标志;
- 熟练处理ES6中对Unicode的转义操作。