一、JavaScript中的String类型用于表示文本型的数据。它是由无符号整数值(16bit)作为元素而组成的集合。
二、在 JavaScript 中,文本数据被以字符串形式存储,单个字符没有单独的类型。
三、字符串中的每个元素在字符串中占据一个位置。
1、索引值/index值从0开始。
2、字符串的长度就是字符串中所含的元素个数。
四、字符串的内部格式始终是UTF-16,它不依赖于页面编码。
包含字符串的方式
一、在 JavaScript 中,有三种包含字符串的方式
1、双引号:””
2、单引号:’’
3、反引号:``
【示例1】
let str = "Hello";
let str2 = 'Single quotes are ok too';
let phrase = `can embed another ${str}`;
二、双引号和单引号都是“简单”引用,在 JavaScript 中两者几乎没有什么差别。
二、反引号是功能扩展引号。它们允许我们通过将变量和表达式包装在${…}中,来将它们嵌入到字符串中。
【示例1】
let name = "John";
// 嵌入一个变量
alert( `Hello, ${name}!` ); // Hello, John!
// 嵌入一个表达式
alert( `the result is ${1 + 2}` ); // the result is 3
1、${…}内的表达式会被计算,计算结果会成为字符串的一部分。
2、可以在${…}内放置任何东西:诸如名为name的变量,或者诸如1 + 2的算数表达式,或者其他一些更复杂的。
3、这仅仅在反引号内有效,其他引号不允许这种嵌入。
alert( "the result is ${1 + 2}" ); // the result is ${1 + 2}(使用双引号则不会计算
创建字符串的方式
String字面量
一、可以使用单引号或双引号创建简单的字符串
'foo'
"bar"
二、可以使用转义序列来创建更复杂的字符串
1、16进制转义序列
\x之后的数值将被认为是16进制数
'\xA9' // "©"
2、Unicode转义序列
Unicode转义序列在\u之后需要至少4个字符
'\u00A9' // "©"
3、Unicode字元逸出
(1)这是ECMAScript6中的新特性。
(2)有了Unicode字元逸出,任何字符都可以用16进制数转义,这使得通过Unicode转义表示大于0x10FFFF的字符成为可能。使用简单的Unicode转义时通常需要分别写字符相应的两个部分(大于0x10FFFF的字符需要拆分为相应的两个小于0x10FFFFF的部分)来达到相同的效果。
'\u{2F804}'
// the same with simple Unicode escapes
'\uD87E\uDC04'
String对象 / 字符串对象
见String对象:https://www.yuque.com/tqpuuk/yrrefz/wvo58h
文本格式化
多行模板字符串
一、模板字符串是一种允许内嵌表达式的String字面值。可以用它实现多行字符串或者字符串内插等特性。
二、模板字符串使用反勾号(``)包裹内容而不是单引号或双引号。
三、模板字符串可以包含占位符。占位符用美元符号和花括号标识(${expression})
多行
一、创建多行字符串
1、使用一般字符串时,创建多行的字符串
console.log("string text line 1\n\
string text line 2")
// "string text line 1
// string text line 2"
2、使用模板字符串时,创建多行的字符串
console.log(`string text line 1
string text line 2`)
// "string text line 1
// string text line 2"
嵌入表达式
一、嵌入表达式
1、在一般的字符串中嵌入表达式
const five = 5
const ten = 10
console.log('Fifteen is ' + (five + ten) + ' and not ' + (2 * five + ten) + '.')
// "Fifteen is 15 and not 20"
2、使用模板字符串,可以使用语法糖让类似功能的实现代码更具可读性。
const five = 5
const ten =10
console.log(`Fifteen is ${five + ten} and not ${2 * five + ten}.`)
// Fifteen is 15 and not 20
国际化
一、Intl对象是ECMAScript国际化API的命名空间,它提供了语言敏感的字符串比较,数字格式化和日期时间格式化功能。
Intl对象的属性
属性 | 描述 |
---|---|
Collator对象的构造函数 | |
NumberFormat对象的构造函数 | |
DateTimeFormat对象的构造函数 |
定序 Collator
一、Collator对象在字符串比较和排序方面很有用。
【示例1】德语中有两种不同的排序方式,电话本(phonebook)和字典(dictionary)。
电话本排序强调发音,比如在排序前 “ä”, “ö”等被扩展为 “ae”, “oe”等发音。
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"
【示例2】有些德语包含变音,所以在字典中忽略变音进行排序是合理的(除非待排序的单词只有变音部分不同:schon 先于 schön)
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"
数字格式化 NumberFormat
一、NumberFormat对象在数字的格式化方面很有用,比如货币数量值
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)); // ¥ 一,三一四.二五
日期和时间格式化 DateTimeFormat
一、DateTimeFormat对象在日期和时间的格式化方面很有用。
【示例1】把一个日期格式化为美式英语格式(不同时区结果不同)
const msPerDay = 24 * 60 * 60 * 1000;
// July 17, 2014 00:00:00 UTC.
const july172014 = new Date(msPerDay * (44 * 365 + 11 + 197));//2014-1970=44年
//这样创建日期真是醉人。。。还要自己计算天数。。。11是闰年中多出的天数。。。
//197是6×30+16(7月的16天)+3(3个大月)-2(2月少2天)
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;
console.log(americanDateTime(july172014)); // 07/16/14, 5:00 PM PDT
正则表达式
见:https://www.yuque.com/tqpuuk/yrrefz/tezl6q
特殊字符
一、可以通过使用“换行符(newline character)”,以支持使用单引号和双引号来创建跨行字符串。
1、换行符写作\n,用来表示换行:
let guestList = "Guests:\n * John\n * Pete\n * Mary";
alert(guestList); // 一个多行的客人列表
2、这两行描述的是一样的,只是书写方式不同:
let str1 = "Hello\nWorld"; // 使用“换行符”创建的两行字符串
// 使用反引号和普通的换行创建的两行字符串
let str2 = `Hello
World`;
alert(str1 == str2); // true
二、其他不常见的“特殊”字符。完整列表:
字符 | 描述 |
---|---|
\n | 换行 |
回车:不单独使用。Windows 文本文件使用两个字符\n的组合来表示换行。 | |
\‘, \“ | 引号 |
\\ | 反斜线 |
\t | 制表符 |
\b, \f, \v | 退格,换页,垂直标签 —— 为了兼容性,现在已经不使用了。 |
\xXX | 具有给定十六进制 Unicode XX 的 Unicode 字符,例如:’\x7A’ 和 ‘z’ 相同。 |
\uXXXX | 以 UTF-16 编码的十六进制代码XXXX的 unicode 字符,例如\u00A9——是版权符号©的 unicode。它必须正好是 4 个十六进制数字。 |
\u{X…XXXXXX} (1 到 6 个十六进制字符) |
具有给定 UTF-32 编码的 unicode 符号。一些罕见的字符用两个 unicode 符号编码,占用 4 个字节。这样我们就可以插入长代码了。 |
【示例1】unicode 示例:
alert( "\u00A9" ); // ©
alert( "\u{20331}" ); // 佫,罕见的中国象形文字(长 unicode)
alert( "\u{1F60D}" ); // 😍,笑脸符号(另一个长 unicode)
三、所有的特殊字符都以反斜杠字符\开始。它也被称为“转义字符”。
【示例1】如果我们想要在字符串中插入一个引号,我们也会使用它。
alert( 'I\'m the Walrus!' ); // I'm the Walrus!
1、正如你所看到的,我们必须在内部引号前加上反斜杠\’,否则它将表示字符串结束。
2、当然,只有与外部闭合引号相同的引号才需要转义。因此,作为一个更优雅的解决方案,我们可以改用双引号或者反引号:
alert( `I'm the Walrus!` ); // I'm the Walrus!
3、注意反斜杠\在 JavaScript 中用于正确读取字符串,然后消失。内存中的字符串没有\。你从上述示例中的alert可以清楚地看到这一点。
4、但是如果我们需要在字符串中显示一个实际的反斜杠\应该怎么做?
(1)我们可以这样做,只需要将其书写两次\:
alert( `The backslash: \\` ); // The backslash: \
比较字符串
【见】比较运算符#字符串比较:https://www.yuque.com/tqpuuk/yrrefz/wza8dh#nbIY2
内部,Unicode
一、进阶内容:这部分会深入字符串内部。如果你计划处理 emoji、罕见的数学或象形文字或其他罕见的符号,这些知识会对你有用。
二、如果你不打算支持它们,你可以跳过这一部分。
代理对
一、所有常用的字符都是一个 2 字节的代码。大多数欧洲语言,数字甚至大多数象形文字中的字母都有 2 字节的表示形式。
二、但 2 字节只允许 65536 个组合,这对于表示每个可能的符号是不够的。所以稀有的符号被称为“代理对”的一对 2 字节的符号编码。
三、这些符号的长度是2:
alert( '𝒳'.length ); // 2,大写数学符号 X
alert( '😂'.length ); // 2,笑哭表情
alert( '𩷶'.length ); // 2,罕见的中国象形文字
四、注意,代理对在 JavaScript 被创建时并不存在,因此无法被编程语言正确处理!
五、我们实际上在上面的每个字符串中都有一个符号,但length显示长度为2。
六、String.fromCodePoint和str.codePointAt是几种处理代理对的少数方法。它们最近才出现在编程语言中。在它们之前,只有String.fromCharCode和str.charCodeAt。这些方法实际上与fromCodePoint/codePointAt相同,但是不适用于代理对。
七、获取符号可能会非常麻烦,因为代理对被认为是两个字符:
alert( '𝒳'[0] ); // 奇怪的符号……
alert( '𝒳'[1] ); // ……代理对的一块
八、请注意,代理对的各部分没有任何意义。因此,上述示例中的 alert 显示的实际上是垃圾信息。
九、技术角度来说,代理对也是可以通过它们的代码检测到的:如果一个字符的代码在0xd800..0xdbff范围内,那么它是代理对的第一部分。下一个字符(第二部分)必须在0xdc00..0xdfff范围中。这些范围是按照标准专门为代理对保留的。
十、在上述示例中:
// charCodeAt 不理解代理对,所以它给出了代理对的代码
alert( '𝒳'.charCodeAt(0).toString(16) ); // d835,在 0xd800 和 0xdbff 之间
alert( '𝒳'.charCodeAt(1).toString(16) ); // dcb3, 在 0xdc00 和 0xdfff 之间
变音符号与规范化
一、在许多语言中,都有一些由基本字符组成的符号,在其上方/下方有一个标记。
【示例1】字母a可以是àáâäãåā的基本字符。最常见的“复合”字符在 UTF-16 表中都有自己的代码。但不是全部,因为可能的组合太多。
二、为了支持任意组合,UTF-16 允许我们使用多个 unicode 字符:基本字符紧跟“装饰”它的一个或多个“标记”字符。
【示例1】如果我们S后跟有特殊的 “dot above” 字符(代码\u0307),则显示 Ṡ。
alert( 'S\u0307' ); // Ṡ
三、如果我们需要在字母上方(或下方)添加额外的标记 —— 没问题,只需要添加必要的标记字符即可。
【示例1】如果我们追加一个字符 “dot below”(代码\u0323),那么我们将得到“S 上面和下面都有点”的字符:Ṩ。
【示例2】
alert( 'S\u0307\u0323' ); // Ṩ
四、这在提供良好灵活性的同时,也存在一个有趣的问题:两个视觉上看起来相同的字符,可以用不同的 unicode 组合表示。
例如:
let s1 = 'S\u0307\u0323'; // Ṩ,S + 上点 + 下点
let s2 = 'S\u0323\u0307'; // Ṩ,S + 下点 + 上点
alert( `s1: ${s1}, s2: ${s2}` );
alert( s1 == s2 ); // false,尽管字符看起来相同
五、为了解决这个问题,有一个 “unicode 规范化”算法,它将每个字符串都转化成单个“通用”格式。
它由str.normalize()实现。
alert( "S\u0307\u0323".normalize() == "S\u0323\u0307".normalize() ); // true
六、有趣的是,在实际情况下,normalize()实际上将一个由 3 个字符组成的序列合并为一个:\u1e68(S 有两个点)。
alert( "S\u0307\u0323".normalize().length ); // 1
alert( "S\u0307\u0323".normalize() == "\u1e68" ); // true
七、事实上,情况并非总是如此,因为符号Ṩ是“常用”的,所以 UTF-16 创建者把它包含在主表中并给它了对应的代码。
如果你想了解更多关于规范化规则和变体的信息 —— 它们在 Unicode 标准附录中有详细描述:Unicode 规范化形式,但对于大多数实际目的来说,本文的内容就已经足够了。