开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情
ES6 字符串的新增方法
String.fromCodePoint() 方法
ES5 提供的 String.fromCharCode() 方法,用于从 Unicode 码点返回对应字符,但是这个方法不能识别码点大于 0xFFFF 的字符。
String.fromCharCode(0x20BB7) // "ஷ"
上面代码中,String.fromCharCode() 不能识别大于 0xFFFF 的码点,所以 0x20BB7 就发生了溢出,最高位 2 被舍弃了,最后返回码点 U+0BB7 对应的字符,而不是码点 U+20BB7 对应的字符。
ES6 提供了 String.fromCodePoint() 方法,可以识别大于 0xFFFF 的字符,弥补了 String.fromCharCode() 方法的不足。在作用上,正好与下面的 codePointAt() 方法相反。
String.fromCodePoint(0x20BB7)
// "?"
String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y'
// true
上面代码中,如果 String.fromCodePoint() 方法有多个参数,则它们会被合并成一个字符串返回。
> 注意,fromCodePoint 方法定义在 String 对象上,而 codePointAt() 方法定义在字符串的实例对象上。
String.raw() 方法
ES6 还为原生的 String 对象,提供了一个 raw() 方法。该方法返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,往往用于模板字符串的处理方法。
String.raw`Hi\n${2+3}!`
// 实际返回 "Hi\n5!",显示的是转义后的结果 "Hi\n5!"
String.raw`Hi\u000A!`;
// 实际返回 "Hi\u000A!",显示的是转义后的结果 "Hi\u000A!"
如果原字符串的斜杠已经转义,那么 String.raw() 会进行再次转义。
String.raw`Hi\n`
// 返回 "Hi\\n"
String.raw`Hi\n` === "Hi\\n" // true
String.raw() 方法可以作为处理模板字符串的基本方法,它会将所有变量替换,而且对斜杠进行转义,方便下一步作为字符串来使用。
String.raw() 本质上是一个正常的函数,只是专用于模板字符串的标签函数。如果写成正常函数的形式,它的第一个参数,应该是一个具有 raw 属性的对象,且 raw 属性的值应该是一个数组,对应模板字符串解析后的值。
// `foo${1 + 2}bar`
// 等同于
String.raw({ raw: ['foo', 'bar'] }, 1 + 2) // "foo3bar"
上面代码中,String.raw() 方法的第一个参数是一个对象,它的 raw 属性等同于原始的模板字符串解析后得到的数组。
作为函数,String.raw() 的代码实现基本如下。
String.raw = function (strings, ...values) {
let output = '';
let index;
for (index = 0; index < values.length; index++) {
output += strings.raw[index] + values[index];
}
output += strings.raw[index]
return output;
}
codePointAt() 方法
JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为 2 个字节。对于那些需要 4 个字节储存的字符(Unicode 码点大于 0xFFFF 的字符),JavaScript 会认为它们是两个字符。
var s = "?";
s.length // 2
s.charAt(0) // ''
s.charAt(1) // ''
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271
上面代码中,汉字“?”(注意,这个字不是“吉祥”的“吉”)的码点是 0x20BB7,UTF-16 编码为 0xD842 0xDFB7(十进制为55362 57271),需要4个字节储存。对于这种4个字节的字符,JavaScript 不能正确处理,字符串长度会误判为 2,而且 charAt() 方法无法读取整个字符,charCodeAt() 方法只能分别返回前两个字节和后两个字节的值。
ES6 提供了 codePointAt() 方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。
let s = '?a';
s.codePointAt(0) // 134071
s.codePointAt(1) // 57271
s.codePointAt(2) // 97
codePointAt() 方法的参数,是字符在字符串中的位置(从 0 开始)。上面代码中,JavaScript 将“?a”视为三个字符,codePointAt() 方法在第一个字符上,正确地识别了“?”,返回了它的十进制码点 134071(即十六进制的20BB7)。在第二个字符(即“?”的后两个字节)和第三个字符“a”上,codePointAt() 方法的结果与 charCodeAt() 方法相同。
总之,codePointAt() 方法会正确返回 32 位的 UTF-16 字符的码点。对于那些两个字节储存的常规字符,它的返回结果与charCodeAt() 方法相同。
codePointAt() 方法返回的是码点的十进制值,如果想要十六进制的值,可以使用 toString() 方法转换一下。
let s = '?a';
s.codePointAt(0).toString(16) // "20bb7"
s.codePointAt(2).toString(16) // "61"
你可能注意到了,codePointAt() 方法的参数,仍然是不正确的。比如,上面代码中,字符 a 在字符串 s 的正确位置序号应该是 1,但是必须向 codePointAt() 方法传入 2。解决这个问题的一个办法是使用 for...of 循环,因为它会正确识别 32 位的 UTF-16 字符。
let s = '?a';
for (let ch of s) {
console.log(ch.codePointAt(0).toString(16));
}
// 20bb7
// 61
另一种方法也可以,使用扩展运算符(...)进行展开运算。
let arr = [...'?a']; // arr.length === 2
arr.forEach(
ch => console.log(ch.codePointAt(0).toString(16))
);
// 20bb7
// 61
codePointAt() 方法是测试一个字符由两个字节还是由四个字节组成的最简单方法。
function is32Bit(c) {
return c.codePointAt(0) > 0xFFFF;
}
is32Bit("?") // true
is32Bit("a") // false