文本格式化

本章介绍在 Javascript 中如何使用字符串与文本内容。

字符串

JavaScript 中的 String 类型用于表示文本型的数据。它是由无符号整数值(16bit)作为元素而组成的集合。字符串中的每个元素在字符串中占据一个位置。第一个元素的 index 值是 0,下一个元素的 index 值是 1,以此类推。字符串的长度就是字符串中所含的元素个数。你可以通过 String 字面值或者 String 对象两种方式创建一个字符串。

String 字面量

可以使用单引号或双引号创建简单的字符串:

js
"foo";
"bar";

可以使用转义序列来创建更复杂的字符串:

16 进制转义序列

\x 之后的数值将被认为是一个 16 进制数。

js
"\xA9"; // "©"

Unicode 转义序列

Unicode 转义序列在\u 之后需要至少 4 个字符。

js
"\u00A9"; // "©"

Unicode 字元逸出

这是 ECMAScript 6 中的新特性。有了 Unicode 字元逸出,任何字符都可以用 16 进制数转义,这使得通过 Unicode 转义表示大于0x10FFFF的字符成为可能。使用简单的 Unicode 转义时通常需要分别写字符相应的两个部分(译注:大于 0x10FFFF 的字符需要拆分为相应的两个小于 0x10FFFF 的部分)来达到同样的效果。

请参阅 String.fromCodePoint()String.prototype.codePointAt()

js
"\u{2F804}";

// the same with simple Unicode escapes
"\uD87E\uDC04";

字符串对象

String 对象是对原始 string 类型的封装 .

js
const foo = new String("foo"); // 创建一个 String 对象
console.log(foo); // 输出:[String: 'foo']
typeof foo; // 返回 'object'

你可以在 String 字面值上使用 String 对象的任何方法—JavaScript 自动把 String 字面值转换为一个临时的 String 对象,然后调用其相应方法,最后丢弃此临时对象。在 String 字面值上也可以使用 String.length 属性。

除非必要,应该尽量使用 String 字面值,因为 String 对象的某些行为可能并不与直觉一致。举例:

js
const firstString = "2 + 2"; //创建一个字符串字面量
const secondString = new String("2 + 2"); // 创建一个字符串对象
eval(firstString); // 返回数字 4
eval(secondString); // 返回包含 "2 + 2" 的字符串对象

String 对象有一个属性 length,标识了字符串中 UTF-16 的码点个数。举例,下面的代码把 13 赋值给了helloLength,因为 "Hello, World!" 包含 13 个字符,每个字符用一个 UTF-16 码点表示。你可以通过数组的方式访问每一个码点,但你不能修改每个字符,因为字符串是不变的类数组对象:

js
const hello = "Hello, World!";
const helloLength = hello.length;
hello[0] = "L"; // 无效,因为字符串是不变的
hello[0]; // 返回 "H"

Unicode 标量值(Unicode scalar value)大于 U+FFFF 的字符(比如中文、日文、韩文、越南语中的一些不常用的文字或者 emoji 表情)使用 2 个代理码点存储在 UTF-16 中。比如,一个包含 U+20BB7“𠮷”字符的字符串,其长度为 2。使用括号访问单个码点的时候,可能不会得到你所期望的结果,你得到的可能是一个不匹配字符串位置的代理码点,并且其违反了 Unicode 规范。请参阅 String.fromCodePoint()String.prototype.codePointAt()

String对象有许多方法:举例来说有些方法返回字符串本身的变体,如 substringtoUpperCase.

下表总结了 String 对象的方法。

方法 描述
charAt, charCodeAt, codePointAt 返回字符串指定位置的字符或者字符编码。
indexOf, lastIndexOf 分别返回字符串中指定子串的位置或最后位置。
startsWith, endsWith, includes 返回字符串是否以指定字符串开始、结束或包含指定字符串。
concat 连接两个字符串并返回新的字符串。
fromCharCode, fromCodePoint 从指定的 Unicode 值序列构造一个字符串。这是一个 String 类方法,不是实例方法。
split 通过将字符串分离成一个个子串来把一个 String 对象分裂到一个字符串数组中。
slice 从一个字符串提取片段并作为新字符串返回。
substring, substr 分别通过指定起始和结束位置,起始位置和长度来返回字符串的指定子集。
match, replace, search 通过正则表达式来工作。
toLowerCase, toUpperCase 分别返回字符串的小写表示和大写表示。
normalize 按照指定的一种 Unicode 正规形式将当前字符串正规化。
repeat 将字符串内容重复指定次数后返回。
trim 去掉字符串开头和结尾的空白字符。

多行模板字符串

模板字符串是一种允许内嵌表达式的 String 字面值。可以用它实现多行字符串或者字符串内插等特性。

模板字符串使用反勾号 (` `) (grave accent) 包裹内容而不是单引号或双引号。模板字符串可以包含占位符。占位符用美元符号和花括号标识 (${expression}).

多行

源代码中插入的任何新行开始字符都作为模板字符串的内容。使用一般的字符串时,为了创建多行的字符串不得不用如下语法:

js
console.log(
  "string text line 1\n\
string text line 2",
);
// "string text line 1
// string text line 2"

为了实现同样效果的多行字符串,现在可以写成如下形式:

js
console.log(`string text line 1
string text line 2`);
// "string text line 1
// string text line 2"

嵌入表达式

为了在一般的字符串中嵌入表达式,需要使用如下语法:

js
const five = 5;
const ten = 10;
console.log(
  "Fifteen is " + (five + ten) + " and not " + (2 * five + ten) + ".",
);
// "Fifteen is 15 and not 20."

现在,使用模板字符串,可以使用语法糖让类似功能的实现代码更具可读性:

js
const five = 5;
const ten = 10;
console.log(`Fifteen is ${five + ten} and not ${2 * five + ten}.`);
// "Fifteen is 15 and not 20."

更多信息,请阅读 JavaScript reference 中的 Template literals

国际化

Intl 对象是 ECMAScript 国际化 API 的命名空间,它提供了语言敏感的字符串比较,数字格式化和日期时间格式化功能。Collator, NumberFormat, 和 DateTimeFormat 对象的构造函数是Intl对象的属性。

日期和时间格式化

DateTimeFormat 对象在日期和时间的格式化方面很有用。下面的代码把一个日期格式化为美式英语格式。(不同时区结果不同.)

js
// July 17, 2014 00:00:00 UTC:
const july172014 = new Date("2014-07-17");

const options = {
  year: "2-digit",
  month: "2-digit",
  day: "2-digit",
  hour: "2-digit",
  minute: "2-digit",
  timeZoneName: "short",
};
const americanDateTime = new Intl.DateTimeFormat("en-US", options).format;

// 本地时区会根据你的设置而有所不同。
// 在 CEST(中欧夏令时)中,输出:07/17/14, 02:00 AM GMT+2。
// 在 PDT(太平洋夏令时)中,输出:07/16/14, 05:00 PM GMT-7。
console.log(americanDateTime(july172014));

数字格式化

NumberFormat 对象在数字的格式化方面很有用,比如货币数量值。

js
var gasPrice = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  minimumFractionDigits: 3,
});

console.log(gasPrice.format(5.259)); // $5.259

var hanDecimalRMBInChina = new Intl.NumberFormat("zh-CN-u-nu-hanidec", {
  style: "currency",
  currency: "CNY",
});

console.log(hanDecimalRMBInChina.format(1314.25)); // ¥ 一,三一四。二五

定序

Collator 对象在字符串比较和排序方面很有用。

举例,德语中有两种不同的排序方式 电话本(phonebook) 和 字典(dictionary). 电话本排序强调发音,比如在排序前“ä”, “ö”等被扩展为“ae”, “oe”等发音。

js
var names = ["Hochberg", "Hönigswald", "Holzman"];

var germanPhonebook = new Intl.Collator("de-DE-u-co-phonebk");

// as if sorting ["Hochberg", "Hoenigswald", "Holzman"]:
console.log(names.sort(germanPhonebook.compare).join(", "));
// logs "Hochberg, Hönigswald, Holzman"

有些德语词包含变音,所以在字典中忽略变音进行排序是合理的 (除非待排序的单词只有变音部分不同:schon 先于 schön).

js
var germanDictionary = new Intl.Collator("de-DE-u-co-dict");

// as if sorting ["Hochberg", "Honigswald", "Holzman"]:
console.log(names.sort(germanDictionary.compare).join(", "));
// logs "Hochberg, Holzman, Hönigswald"

关于Intl API 的更多信息,请参考 Introducing the JavaScript Internationalization API