ES6学习笔记11:Unicode编码

JavaScript中字符编码的基本知识

在JavaScript中,所有的编码都是采用UTF-16进行编码的(大多数引擎,少部分使用UCS-2),也就是2个字节(16位)表示一个编码单元。

大多数情况下,一个编码单元就可以表示一个完整的字符,如英文字母,数字等;但16位能表示的编码单元有限,有些字符需要通过两个编码单元才能表示一个字符,如汉字,特殊字符等。一个编码单元就能表示的字符被称为基本面(Basic Multilingual Plane),需要两个编码单元的字符被称为补充面(Supplementary planes or Astral planes)。

这样,基本面和补充面使得所有字符都对应一个唯一标识符,这种标识符被称为码点(Code Point)。

ES5中的问题

在ES5中,所有对于字符的操作都是基于16位的,那么当遇到超过这个范围的字符时就变得无能为力了。

1
2
3
4
5
6
7
var ch = '🇨🇳';
console.log(ch.length); // 4
console.log(/^(..))$/.test(ch)); // false
console.log(ch.charAt(0)); // '🇨'
console.log(ch.charAt(1)); // '🇳'
console.log(ch.charCodeAt(0)); // 55356
console.log(ch.charCodeAt(1)); // 56808

可以看出,上面所有的操作都没有把ch当做一个字符去处理。

当对象是一个由两个16位编码单元组成的代理对(Representation)字符时,
首先,长度为4;
正则表达式测定两个字节也为false
charAt()也不能处理字符串中第一次出现代理对字符之后的所有字符(注意出现之前是可以的);
最后charCodeAt()也将一个代理对字符当做两个16位字符对待。

ES6中的解决办法

在ES6中,对于代理对字符新增了相关的字符串处理方法String.codePointAt()String.fromCodePoint()。从字面意思就可以知道,第一个方法是用于查找码点所在位置,第二方法是将一个码点位置转换为编码的,这两个方法都对代理对字符有效。

string.codePointAt()

1
2
3
4
5
6
7
const chStr = '🇨🇳';
console.log(chStr.charCodeAt(0)); // 55356
console.log(chStr.charCodeAt(1)); // 56808
console.log(chStr.charCodeAt(2)); // 55356
console.log(chStr.charCodeAt(3)); // 56819
console.log(chStr.codePointAt(0)); // 127464
console.log(chStr.codePointAt(2)); // 127475

从对下标为2的字符处理的区别可以看出,string.codePointAt()确实将一个代理对字符当做一个码点处理。

String.fromCodePoint()

1
console.log(`${String.fromCodePoint(127464)}${String.fromCodePoint(127475)}`); // 🇨🇳

一个代理对码点也通过String.fromCodePoint()正确地转换成了代理对字符。

正则表达式

ES6中定义了一个新的正则标志u,用于处理Unicode字符。

1
2
console.log(/^(..)$/.test('🇨🇳')); // false
console.log(/^(..)$/u.test('🇨🇳')); // true

从上面的代码我们可以看出加上u之后,正则表达式将'🇨🇳'看成了一个完整的字符,正则将字符串中的字符视作基本操作单位,而不是编码单元作为基本操作单位。

转义代理对字符

在ES5中,前缀加\u可以将Unicode转码,但是并不能正确处理代理对字符。在ES6中可以正确处理了,只需要在ES5的基础上加上一对大括号就可以正确转码。

1
console.log('\u{1f1e8}\u{1f1f3}'); // 🇨🇳

知识点总结

  1. 了解JavaScript的基本编码知识;
  2. 熟练使用ES6中codePointAt()和fromCodePoint()两个处理代理对字符的函数;
  3. 熟练使用ES6中正则表达式中处理Unicode的u标志;
  4. 熟练处理ES6中对Unicode的转义操作。

参考

  1. BabelJS - Learn ES2015
  2. ES6中的Unicode编码知识 - GitHub
  3. 詳解 ES6 Unicode - andyyu0920
  4. JavaScript的内部字符编码是UCS-2还是UTF-16 - 卢林