9、字符串

最后更新于:2022-04-02 06:07:11

  在介绍字符串之前,有必要先了解一点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+0000到U+10FFFF,由于包含的字符众多,因此会把它们划分成17组,组也叫平面(Plane),每个平面包含2^16=65536个字符,其中第0个平面叫做基本多语言平面(Basic Multilingual Plane,简称BMP),码位范围从U+0000到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+0000到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”是方法的默认值。 :-: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/0fee91db9eeabbf1d59f8fb9742ee635_600x370.png) :-: 表4 标准化参数   上表中的标准等价(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()的理想替代品。 :-: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/f847374d80a3c05e00b8460a68f4ce17_600x285.png) :-: 表5 新的检索方法   三个方法都能接收两个参数,先介绍第一个参数,表示要检索的子串,注意,子串不能是正则表达式,下面展示了只传一个参数时的情况。 ~~~ 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篇模板字面量](https://www.cnblogs.com/strick/p/10173486.html)的标签模板中曾提到过。不过当时只强调了它是一个内置的标签模板,用于获取原始信息,但其实它也可以作为普通的函数来使用。只不过它的第一个参数得是一个包含raw属性的对象,raw属性的值既可以是数组也可以是字符串,第二个是可选的剩余参数,这些参数可插到指定位置,例如方法的第二个参数需要插到raw属性值中的第一和第二个元素之间,具体可参考下面的例子。 ~~~ String.raw({raw: "abc"}, 0, 1, 2);  //"a0b1c" //相当于 String.raw({raw: ["a", "b", "c"]}, 0, 1, 2); //"a0b1c" ~~~ ***** > 原文出处: [博客园-ES6躬行记](https://www.cnblogs.com/strick/category/1372951.html) [知乎专栏-ES6躬行记](https://zhuanlan.zhihu.com/pwes6) 已建立一个微信前端交流群,如要进群,请先加微信号freedom20180706或扫描下面的二维码,请求中需注明“看云加群”,在通过请求后就会把你拉进来。还搜集整理了一套[面试资料](https://github.com/pwstrick/daily),欢迎浏览。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200)
';