字符串和字符码

这个数据集的一种用途是确定一段文本所使用的脚本。 我们来看看执行它的程序。

请记住,每个脚本都有一组与其相关的字符码范围。 所以给定一个字符码,我们可以使用这样的函数来找到相应的脚本(如果有的话):

  1. function characterScript(code) {
  2. for (let script of SCRIPTS) {
  3. if (script.ranges.some(([from, to]) => {
  4. return code >= from && code < to;
  5. })) {
  6. return script;
  7. }
  8. }
  9. return null;
  10. }
  11. console.log(characterScript(121));
  12. // → {name: "Latin", …}

some方法是另一个高阶函数。 它需要一个测试函数,并告诉你该函数是否对数组中的任何元素返回true

但是,我们如何获得字符串中的字符码?

在第一章中,我提到 JavaScript 字符串被编码为一个 16 位数字的序列。 这些被称为代码单元。 一个 Unicode 字符代码最初应该能放进这样一个单元(它给你超 65,000 个字符)。 后来人们发现它不够用了,很多人避开了为每个字符使用更多内存的需求。 为了解决这些问题,人们发明了 UTF-16,JavaScript 字符串使用的格式 。它使用单个 16 位代码单元描述了大多数常见字符,但是为其他字符使用一对两个这样的单元。

今天 UTF-16 通常被认为是一个糟糕的主意。 它似乎总是故意设计来引起错误。 很容易编写程序,假装代码单元和字符是一个东西。 如果你的语言不使用两个单位的字符,显然能正常工作。 但只要有人试图用一些不太常见的中文字符来使用这样的程序,就会中断。 幸运的是,随着 emoji 符号的出现,每个人都开始使用两个单元的字符,处理这些问题的负担更加分散。

  1. // Two emoji characters, horse and shoe
  2. let horseShoe = "\ud83d\udc34\ud83d\udc5f";
  3. console.log(horseShoe.length);
  4. // → 4
  5. console.log(horseShoe[0]);
  6. // → (Invalid half-character)
  7. console.log(horseShoe.charCodeAt(0));
  8. // → 55357 (Code of the half-character)
  9. console.log(horseShoe.codePointAt(0));
  10. // → 128052 (Actual code for horse emoji)

JavaScript的charCodeAt方法为你提供了一个代码单元,而不是一个完整的字符代码。 稍后添加的codePointAt方法确实提供了完整的 Unicode 字符。 所以我们可以使用它从字符串中获取字符。 但传递给codePointAt的参数仍然是代码单元序列的索引。 因此,要运行字符串中的所有字符,我们仍然需要处理一个字符占用一个还是两个代码单元的问题。

在上一章中,我提到for/of循环也可以用在字符串上。 像codePointAt一样,这种类型的循环,是在人们敏锐地意识到 UTF-16 的问题的时候引入的。 当你用它来遍历一个字符串时,它会给你真正的字符,而不是代码单元。

  1. let roseDragon = "\ud83c\udf45\ud83d\udc09";
  2. for (let char of roseDragon) {
  3. console.log(char);
  4. // → (emoji rose)
  5. // → (emoji dragon)

如果你有一个字符(它是一个或两个代码单元的字符串),你可以使用codePointAt(0)来获得它的代码。