实例方法:codePointAt()

JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode 码点大于0xFFFF的字符),JavaScript 会认为它们是两个字符。

  1. var s = "𠮷";
  2. s.length // 2
  3. s.charAt(0) // ''
  4. s.charAt(1) // ''
  5. s.charCodeAt(0) // 55362
  6. s.charCodeAt(1) // 57271

上面代码中,汉字“𠮷”(注意,这个字不是“吉祥”的“吉”)的码点是0x20BB7,UTF-16 编码为0xD842 0xDFB7(十进制为55362 57271),需要4个字节储存。对于这种4个字节的字符,JavaScript 不能正确处理,字符串长度会误判为2,而且charAt()方法无法读取整个字符,charCodeAt()方法只能分别返回前两个字节和后两个字节的值。

ES6 提供了codePointAt()方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。

  1. let s = '𠮷a';
  2. s.codePointAt(0) // 134071
  3. s.codePointAt(1) // 57271
  4. s.codePointAt(2) // 97

codePointAt()方法的参数,是字符在字符串中的位置(从 0 开始)。上面代码中,JavaScript 将“𠮷a”视为三个字符,codePointAt 方法在第一个字符上,正确地识别了“𠮷”,返回了它的十进制码点 134071(即十六进制的20BB7)。在第二个字符(即“𠮷”的后两个字节)和第三个字符“a”上,codePointAt()方法的结果与charCodeAt()方法相同。

总之,codePointAt()方法会正确返回 32 位的 UTF-16 字符的码点。对于那些两个字节储存的常规字符,它的返回结果与charCodeAt()方法相同。

codePointAt()方法返回的是码点的十进制值,如果想要十六进制的值,可以使用toString()方法转换一下。

  1. let s = '𠮷a';
  2. s.codePointAt(0).toString(16) // "20bb7"
  3. s.codePointAt(2).toString(16) // "61"

你可能注意到了,codePointAt()方法的参数,仍然是不正确的。比如,上面代码中,字符a在字符串s的正确位置序号应该是 1,但是必须向codePointAt()方法传入 2。解决这个问题的一个办法是使用for...of循环,因为它会正确识别 32 位的 UTF-16 字符。

  1. let s = '𠮷a';
  2. for (let ch of s) {
  3. console.log(ch.codePointAt(0).toString(16));
  4. }
  5. // 20bb7
  6. // 61

另一种方法也可以,使用扩展运算符(...)进行展开运算。

  1. let arr = [...'𠮷a']; // arr.length === 2
  2. arr.forEach(
  3. ch => console.log(ch.codePointAt(0).toString(16))
  4. );
  5. // 20bb7
  6. // 61

codePointAt()方法是测试一个字符由两个字节还是由四个字节组成的最简单方法。

  1. function is32Bit(c) {
  2. return c.codePointAt(0) > 0xFFFF;
  3. }
  4. is32Bit("𠮷") // true
  5. is32Bit("a") // false