字符串和字符码
这个数据集的一种用途是确定一段文本所使用的脚本。 我们来看看执行它的程序。
请记住,每个脚本都有一组与其相关的字符码范围。 所以给定一个字符码,我们可以使用这样的函数来找到相应的脚本(如果有的话):
function characterScript(code) {
for (let script of SCRIPTS) {
if (script.ranges.some(([from, to]) => {
return code >= from && code < to;
})) {
return script;
}
}
return null;
}
console.log(characterScript(121));
// → {name: "Latin", …}
some
方法是另一个高阶函数。 它需要一个测试函数,并告诉你该函数是否对数组中的任何元素返回true
。
但是,我们如何获得字符串中的字符码?
在第一章中,我提到 JavaScript 字符串被编码为一个 16 位数字的序列。 这些被称为代码单元。 一个 Unicode 字符代码最初应该能放进这样一个单元(它给你超 65,000 个字符)。 后来人们发现它不够用了,很多人避开了为每个字符使用更多内存的需求。 为了解决这些问题,人们发明了 UTF-16,JavaScript 字符串使用的格式 。它使用单个 16 位代码单元描述了大多数常见字符,但是为其他字符使用一对两个这样的单元。
今天 UTF-16 通常被认为是一个糟糕的主意。 它似乎总是故意设计来引起错误。 很容易编写程序,假装代码单元和字符是一个东西。 如果你的语言不使用两个单位的字符,显然能正常工作。 但只要有人试图用一些不太常见的中文字符来使用这样的程序,就会中断。 幸运的是,随着 emoji 符号的出现,每个人都开始使用两个单元的字符,处理这些问题的负担更加分散。
// Two emoji characters, horse and shoe
let horseShoe = "\ud83d\udc34\ud83d\udc5f";
console.log(horseShoe.length);
// → 4
console.log(horseShoe[0]);
// → (Invalid half-character)
console.log(horseShoe.charCodeAt(0));
// → 55357 (Code of the half-character)
console.log(horseShoe.codePointAt(0));
// → 128052 (Actual code for horse emoji)
JavaScript的charCodeAt
方法为你提供了一个代码单元,而不是一个完整的字符代码。 稍后添加的codePointAt
方法确实提供了完整的 Unicode 字符。 所以我们可以使用它从字符串中获取字符。 但传递给codePointAt
的参数仍然是代码单元序列的索引。 因此,要运行字符串中的所有字符,我们仍然需要处理一个字符占用一个还是两个代码单元的问题。
在上一章中,我提到for/of
循环也可以用在字符串上。 像codePointAt
一样,这种类型的循环,是在人们敏锐地意识到 UTF-16 的问题的时候引入的。 当你用它来遍历一个字符串时,它会给你真正的字符,而不是代码单元。
let roseDragon = "\ud83c\udf45\ud83d\udc09";
for (let char of roseDragon) {
console.log(char);
// → (emoji rose)
// → (emoji dragon)
如果你有一个字符(它是一个或两个代码单元的字符串),你可以使用codePointAt(0)
来获得它的代码。