String

​ String 是对应字符串的引用类型。要创建一个 String 对象,使用 String 构造函数并传入一个数值,如下例所示:

let stringObject = new String("hello world");

​ String 对象的方法可以在所有字符串原始值上调用。3 个继承的方法 valueOf()、toLocaleString() 和 toString()都返回对象的原始字符串值

​ 每个 String 对象都有一个 length 属性,表示字符串中字符的数量。来看下面的例子:

let stringValue = "hello world";
console.log(stringValue.length); // "11"

​ 这个例子输出了字符串”hello world”中包含的字符数量:11。注意,即使字符串中包含双字节字符(而不是单字节的 ASCII 字符),也仍然会按单字符来计数

​ String 类型提供了很多方法来解析和操作字符串

JavaScript 字符

​ JavaScript 字符串由 16 位码元(code unit)组成。对多数字符来说,每 16 位码元对应一个字符。换句话说,字符串的 length 属性表示字符串包含多少 16 位码元:

let message = "abcde";
console.log(message.length); // 5

​ 此外,charAt()方法返回给定索引位置的字符,由传给方法的整数参数指定。具体来说,这个方法查找指定索引位置的 16 位码元,并返回该码元对应的字符:

let message = "abcde";
console.log(message.charAt(2)); // "c"

​ JavaScript 字符串使用了两种 Unicode 编码混合的策略:UCS-2 和 UTF-16。对于可以采用 16 位编码 的字符(U+0000~U+FFFF),这两种编码实际上是一样的。

注意 要深入了解关于字符编码的内容,推荐 Joel Spolsky 写的博客文章:“The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)”。

另一个有用的资源是 Mathias Bynens 的博文:“JavaScript’s Internal Character Encoding: UCS-2 or UTF-16?”。

​ 使用 charCodeAt()方法可以查看指定码元的字符编码。这个方法返回指定索引位置的码元值,索引以整数指定。比如:

let message = "abcde";
// Unicode "Latin small letter C"的编码是U+0063 
console.log(message.charCodeAt(2)); // 99
// 十进制99等于十六进制63
console.log(99 === 0x63); // true

​ fromCharCode()方法用于根据给定的 UTF-16 码元创建字符串中的字符。这个方法可以接受任意多个数值,并返回将所有数值对应的字符拼接起来的字符串:

// Unicode "Latin small letter A"的编码是U+0061
// Unicode "Latin small letter B"的编码是U+0062
// Unicode "Latin small letter C"的编码是U+0063
// Unicode "Latin small letter D"的编码是U+0064
// Unicode "Latin small letter E"的编码是U+0065
console.log(String.fromCharCode(0x61, 0x62, 0x63, 0x64, 0x65)); // "abcde"
// 0x0061 === 97
// 0x0062 === 98
// 0x0063 === 99
// 0x0064 === 100
// 0x0065 === 101
console.log(String.fromCharCode(97, 98, 99, 100, 101)); // "abcde"

​ 对于 U+0000~U+FFFF 范围内的字符,length、charAt()、charCodeAt()和 fromCharCode() 返回的结果都跟预期是一样的。这是因为在这个范围内,每个字符都是用 16 位表示的,而这几个方法也都基于 16 位码元完成操作。只要字符编码大小与码元大小一一对应,这些方法就能如期工作。

​ 这个对应关系在扩展到 Unicode 增补字符平面时就不成立了。问题很简单,即 16 位只能唯一表示 65 536 个字符。这对于大多数语言字符集是足够了,在 Unicode 中称为基本多语言平面(BMP)。为了表示更多的字符,Unicode 采用了一个策略,即每个字符使用另外 16 位去选择一个增补平面。这种每个字符使用两个 16 位码元的策略称代理对

​ 在涉及增补平面的字符时,前面讨论的字符串方法就会出问题。比如,下面的例子中使用了一个笑脸表情符号,也就是一个使用代理对编码的字符:

// "smiling face with smiling eyes" 表情符号的编码是 U+1F60A // 0x1F60A === 128522
let message = "ab☺de";
console.log(message.length); // 6 
console.log(message.charAt(1)); // b
console.log(message.charAt(2)); // <?>
console.log(message.charAt(3)); // <?>
console.log(message.charAt(4));// d
console.log(message.charCodeAt(1)); // 98
console.log(message.charCodeAt(2)); // 55357
console.log(message.charCodeAt(3)); // 56842
console.log(message.charCodeAt(4));// 100
console.log(String.fromCodePoint(0x1F60A)); // ☺
console.log(String.fromCharCode(97, 98, 55357, 56842, 100, 101)); // ab☺de

​ 这些方法仍然将 16 位码元当作一个字符,事实上索引 2 和索引 3 对应的码元应该被看成一个代理对,只对应一个字符。fromCharCode()方法仍然返回正确的结果,因为它实际上是基于提供的二进制表示直接组合成字符串浏览器可以正确解析代理对(由两个码元构成),并正确地将其识别为一个 Unicode 笑脸字符。

​ 为正确解析既包含单码元字符又包含代理对字符的字符串,可以使用 codePointAt()来代替 charCodeAt()。跟使用 charCodeAt()时类似,codePointAt()接收 16 位码元的索引并返回该索引位置上的码点(code point)。码点是 Unicode 中一个字符的完整标识。比如,“c”的码点是 0x0063,而 ”☺“的码点是 0x1F60A。码点可能是 16 位,也可能是 32 位,而 codePointAt()方法可以从指定码元位置识别完整的码点

let message = "ab☺de";
console.log(message.codePointAt(1)); // 98
console.log(message.codePointAt(2)); // 128522
console.log(message.codePointAt(3)); // 56842
console.log(message.codePointAt(4)); // 100

​ 注意,如果传入的码元索引并非代理对的开头,就会返回错误的码点。这种错误只有检测单个字符的时候才会出现,可以通过从左到右按正确的码元数遍历字符串来规避。迭代字符串可以智能地识别代理对的码点:

console.log([..."ab☺de"]); // ["a", "b", "☺", "d", "e"]

​ 与 charCodeAt()有对应的 codePointAt()一样,fromCharCode()也有一个对应的 fromCodePoint()。 这个方法接收任意数量的码点,返回对应字符拼接起来的字符串:

console.log(String.fromCharCode(97, 98, 55357, 56842, 100, 101)); // ab☺de
console.log(String.fromCodePoint(97, 98, 128522, 100, 101)); // ab☺de

**normalize()**方法

字符串操作方法

​ 本节介绍几个操作字符串值的方法。首先是 concat(),用于将一个或多个字符串拼接成一个新字符串。来看下面的例子:

let stringValue = "hello ";
let result = stringValue.concat("world");
console.log(result); // "hello world" 
console.log(stringValue); // "hello"

​ 在这个例子中,对 stringValue 调用 concat()方法的结果是得到”hello world”,但 stringValue 的值保持不变。concat()方法可以接收任意多个参数,因此可以一次性拼接多个字符串, 如下所示:

let stringValue = "hello ";
let result = stringValue.concat("world", "!");
console.log(result); // "hello world!" 
console.log(stringValue); // "hello"

​ 这个修改后的例子将字符串”world”和”!”追加到了”hello “后面。虽然 concat()方法可以拼接字符串,但更常用的方式是使用加号操作符(+)。而且多数情况下,对于拼接多个字符串来说,使用加号更方便

​ ECMAScript 提供了 3 个从字符串中提取子字符串的方法:slice()、substr()和 substring()。这 3 个方法都返回调用它们的字符串的一个子字符串,而且都接收一或两个参数。第一个参数表示子字符串开始的位置,第二个参数表示子字符串结束的位置。对 slice()和 substring()而言,第二个参数是提取结束的位置(即该位置之前的字符会被提取出来)。对 substr()而言,第二个参数表示返回的子字符串数量。 任何情况下,省略第二个参数都意味着提取到字符串末尾。与 concat()方法一样,slice()、substr() 和 substring()也不会修改调用它们的字符串,而只会返回提取到的原始新字符串值。来看下面的例子:

字符串位置方法

字符串包含方法

**trim()**方法

**repeat()**方法

**padStart()**和 **padEnd()**方法

字符串迭代与解构

字符串大小写转换

字符串模式匹配方法

**localeCompare()**方法

HTML 方法