Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

ES6躬行记(9)——字符串

咖啡机(K.F.J) 2019-02-12 09:28:00 阅读数:211 评论数:0 点赞数:0 收藏数:0

在介绍字符串之前,有必要先了解一点Unicode的基础知识,有助于理解ES6提供的新功能和新特性。

一、Unicode

Unicode是一种字符集(即多个字符的集合),它的目标是涵盖世界上的所有字符,为其提供唯一的标识符,这个标识符叫做码位或码点(Code Point)。码位既可以用一个从0开始计算的数值表示,也可以用U+作为前缀后面紧跟十六进制数表示。

Unicode只规定了每个字符的码位,但并没有规定如何用字节序列(即二进制数字存储方式)表示字符,于是就出现了字符编码(Character Encoding)。Unicode包含多种字符编码,例如UTF-8、UTF-16等,此处的UTF前缀是Unicode Transformation Format的缩写,即统一转换格式,它们都是Unicode的一种实现方式。其中UTF-8是变长编码,使用1~4个字节表示一个字符,它的最小编码单元(Code Unit)为一个字节(即8位);而UTF-16使用2或4个字节表示一个字符,它的最小编码单元为两个字节(即16位)。

Unicode的码位范围从U+到U+10FFFF,由于包含的字符众多,因此会把它们划分成17组,组也叫平面(Plane),每个平面包含2^16=65536个字符,其中第0个平面叫做基本多语言平面(Basic Multilingual Plane,简称BMP),码位范围从U+到U+FFFF(包含了ASCII码),剩下的16个为辅助平面(Supplementary Plane)。

JavaScript采用了UTF-16编码的Unicode字符集,BMP中的字符可用一个16位的编码单元表示,而辅助平面中的字符则要遵循UTF-16的代理对(Surrogate Pair)规则,即用两个编码单元表示。这意味着JavaScript中的一个Unicode字符,它的长度有可能是1,但也有可能是2。由于JavaScript中的字符串方法(例如substring()、charAt()等)都会受到这种编码规则的影响,因此有时候会返回出人意料的结果。不过好在ES6大幅增强了对Unicode的支持,有效避免了这种意外性情况的发生。

二、Unicode字符

在JavaScript中,Unicode字符可以用Unicode转义字符的形式(即\uXXXX)表示,其中4个“X”表示字符的码位,而“X”是一个16进制字符,还要注意一点,ES5只支持4个“X”。也就是说,这种形式只能表示BMP中的字符(即U+到U+FFFF内的字符),如果要使用辅助平面中的字符,那么需要写两个Unicode转义字符。下面代码中,第一个字符是BMP中的“向”,第二个字符是2号平面中的“”。

let word1 = "\u5411";
console.log(word1); //"向"
let word2 = "\ud842\udfb3";
console.log(word2); //""

ES6为Unicode字符提供了一种新形式,只需把码位用花括号包裹,就能支持辅助平面中的字符。下面使用了新形式来描述字符“”。

let word3 = "\u{20BB3}";
console.log(word3); //""

三、Unicode标准化

Unicode标准化(Unicode Normalization),也叫Unicode正规化或Unicode规范化,可将字符转换成指定的字节序列,统一表现形式,以及确定字符之间的等价性。例如字符“ü”,既可以只用U+00FC表示,也可以用U+0075(u)和U+0308(¨)组合表示,虽然对于人类来说,两种表示法得到的结果在视觉上是完全相同的,但对于计算机来说却是不同的,如下所示。

var mark1 = "\u00FC",
mark2 = "\u0075\u0308";
mark1 === mark2; //false

ES6新增了一个原型方法normalize(),可以将字符串标准化,修改上面的例子,就能得到相等的结果,如下所示。

mark1.normalize() === mark2.normalize(); //true

normalize()方法可以接收一个字符串参数,但只有4个可选值(如表4所示),其中“NFC”是方法的默认值。

表4  标准化参数

可选值 作用描述
NFD 标准等价分解
NFC 先以标准等价分解,再以标准等价合成
NFKD 兼容等价分解
NFKC 先以兼容等价分解,再以标准等价合成

上表中的标准等价(Canonical Equivalence)和兼容等价(Compatibility Equivalence)都表示相同的字符或字符序列,并且前者是后者的一个子集。标准等价会保持视觉外观和文本含义,前面字符“ü”的示例就用到了标准等价;而兼容等价会改变视觉外观和文本含义,例如罗马数字十二(Ⅻ)可由一个罗马数十(Ⅹ)和两个罗马数一(Ⅰ)组成,两者只有通过兼容等价的标准化处理后才能匹配成功,如下所示。

var digit1 = "\u216B", //"Ⅻ"
digit2 = "\u2169\u2160\u2160"; //"ⅩⅠⅠ"
digit1 = digit1.normalize("NFKC"); //"XII"
digit2 = digit2.normalize("NFKC"); //"XII"
digit1 === digit2; //true

四、码位的处理

字符串的原型方法charCodeAt()可以读取到BMP中的字符的码位,而辅助平面中的字符却无法正确读取,它们会被当成两个字符来对待。还是以“”为例,如下所示,分别返回字符串第0和第1处位置的码位。

var str = "";
str.charCodeAt(0); //55362
str.charCodeAt(1); //57267

ES6提供了codePointAt()方法,有效解决了上述问题,如下所示。

str.codePointAt(0); //134067
str.codePointAt(1); //57267

不过需要注意,codePointAt()方法还能返回字符的第二个编码单元的码位,即上面代码中第2条语句。

String对象的静态方法fromCharCode()可将码位转换成字符,功能和charCodeAt()方法正好相反,但也不能正确处理辅助平面中的字符。为此,ES6扩展了String对象,新增了一个静态方法fromCodePoint(),和codePointAt()方法对应,如下所示,由于第1条语句得到的结果是一个无法打印的字符,因此没有展示。

String.fromCharCode(134067);
String.fromCodePoint(134067); //""

五、解析字符串

ES6增强了JavaScript解析字符串的能力,新增了3个检索子串的方法(如表5所示),它们都返回布尔值。在某些场景,这些方法是indexOf()的理想替代品。

表5  新的检索方法

方法 功能描述
includes() 判断子串是否存在于字符串中
startsWith() 判断子串是否存在于字符串的头部
endsWith() 判断子串是否存在于字符串的尾部

三个方法都能接收两个参数,先介绍第一个参数,表示要检索的子串,注意,子串不能是正则表达式,下面展示了只传一个参数时的情况。

var str = "My name is strick";
str.length; //17
str.includes("name"); //true
str.startsWith("name"); //false
str.endsWith("name"); //false

方法的第二个参数是一个可选值,它有两种含义。在includes()和startsWith()方法中用于指定检索的起始位置,默认值为0;而在endsWith()方法中用于指定原字符串str的长度,默认值为str.length。修改上面的代码,为startsWith()和endsWith()分别传入第二个参数,前者的值为3,后者的值为7,它们的结果都变成了true,如下所示。

str.startsWith("name", 3); //true
str.endsWith("name", 7); //true

除了检索的新方法,ES6还提供了一个重复字符串的新方法:repeat(),它的参数是一个正整数,表示重复的次数,使用方法如下所示。

"name".repeat(2); //"namename"

最后介绍的是String对象的静态方法raw(),在第4篇模板字面量的标签模板中曾提到过。不过当时只强调了它是一个内置的标签模板,用于获取原始信息,但其实它也可以作为普通的函数来使用。只不过它的第一个参数得是一个包含raw属性的对象,raw属性的值既可以是数组也可以是字符串,第二个是可选的剩余参数,这些参数可插到指定位置,例如方法的第二个参数需要插到raw属性值中的第一和第二个元素之间,具体可参考下面的例子。

String.raw({raw: "abc"}, 0, 1, 2); //"a0b1c"
//相当于
String.raw({raw: ["a", "b", "c"]}, 0, 1, 2); //"a0b1c"

 

版权声明
本文为[咖啡机(K.F.J)]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/strick/p/10174360.html

编程之旅,人生之路,不止于编程,还有诗和远方。
阅代码原理,看框架知识,学企业实践;
赏诗词,读日记,踏人生之路,观世界之行;

支付宝红包,每日可领